简单工厂设计模式&lambda重构简单工厂模式

概念以及背景
简单工厂模式(Simple Factory Pattern):定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。
工厂模式是最常用的一类创建型设计模式,通常我们所说的工厂模式是指工厂方法模式而简单工厂模式并不属于GoF 23个经典设计模式,但通常将它作为学习其他工厂模式的基础。

简单地说就是需要什么对象,传入给定格式的参数,就可以获取所需对象,而调用方无需知道其中创建对象的细节,在创建对象那一方也有自己的实现细节,代码结构清晰、解耦。

本文涉及源码在这里

里面用三种不同的方式应用了简单工厂模式,有用到Java8 Lambda 方式重构简单工厂模式,也有一个我实际开发中应用到的方式。

一、简单工厂模式

UML类图如下,有一个抽象金融产品类AbstractFinanceProduct,有三个子类,贷款(LoanFinanceProduct)、股票(StockFinanceProduct)和债券(BondFinanceProduct)都是抽象金融产品类(AbstractFinanceProduct)的子类。
工厂类是SimpleFactory,负责建对象,根据传入的参数,返回需要的对象。


简单工厂模式.jpg
/**
 * 抽象金融产品类
 */
public abstract class AbstractFinanceProduct {

    /**
     * 所有产品类的公共业务方法
     */
    public void methodSame() {
        String className = this.getClass().getName();
        //公共方法的实现
        System.out.println("抽象类公共方法,该类是:" + className);
    }

    /**
     * 声明抽象业务方法
     */
    public abstract void disPlayProduct();
}

产品名称枚举,用于工厂类创建对象方法的参数,用枚举的话比用常量好,常量的话方法参数调用方可能会给超出产品范围的常量,而用枚举的话产品枚举类范围都是明确的,也不用担心写错,如把股票Stock写成Stick等错误写法。

/**
 * 产品名称枚举
 */
public enum ProductEnum {
    /**
     * 产品类别
     */
    Bond("Bond"), Loan("Loan"), Stock("Stock");

    String name;

    ProductEnum(String name) {
        this.name = name;
    }
}
/**
 * 简单工厂类
 */
class SimpleFactory {

    static AbstractFinanceProduct creatProduct(ProductEnum productEnum) {
        switch (productEnum) {
            case Bond :
                return new BondFinanceProduct();
            case Loan :
                return new LoanFinanceProduct();
            case Stock :
                return new StockFinanceProduct();
            default:
                throw new RuntimeException("No such product" + productEnum.name());
        }
    }
}

工厂类的调用方,只需传入某个产品的类别,即可返回该产品对象。

public class SimpleFactoryPattern {
    public static void main(String[] args) {
        AbstractFinanceProduct stockProduct = SimpleFactory.creatProduct(ProductEnum.Stock);
        AbstractFinanceProduct bondProduct = SimpleFactory.creatProduct(ProductEnum.Bond);
        stockProduct.methodSame();
        stockProduct.disPlayProduct();
        bondProduct.disPlayProduct();
    }
}
二、用Java8 Lambda重构简单工厂模式

跟步骤一的UML类图一样,如下:


用lambda的方式实现简单工厂模式.jpg

用lambda的方式实现简单工厂模式,lambda给我们提供了一些常用的函数式接口可以用,简单工厂模式需要建对象和返回对象,故可用Supplier<T>函数式接口,至于创建所需对象的入参,接着下面看。

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

这里把所需创建对象的入参做成一个Map的key键,而Map的value是一个函数式接口,在设值的时候用lambda实现该value的函数式接口,这里巧妙地用了lambda的方法引用ClassName::new,实现函数式接口Supplier<T>的get()接口方法。

/**
 * 用lambda的方式实现简单工厂模式
 */
class SimpleFactoryLambda {
    /**
     * Map的value是一个函数式接口,里面的接口待实现
     */
    private final static Map<ProductEnum, Supplier<AbstractFinanceProduct>> PRODEUC_MAP = new HashMap<>();
    static {
        //这里put进去的value就是函数式接口的一个实现,lambda表达式的方法引用
        PRODEUC_MAP.put(ProductEnum.Bond, BondFinanceProduct::new);
        PRODEUC_MAP.put(ProductEnum.Stock, StockFinanceProduct::new);
        PRODEUC_MAP.put(ProductEnum.Loan, LoanFinanceProduct::new);
    }

    static AbstractFinanceProduct creatProduct(ProductEnum productEnum) {
        Supplier<AbstractFinanceProduct> productSupplier = PRODEUC_MAP.get(productEnum);
        if (productSupplier != null) {
            //这里每次调用都生成新的对象,map的value得到的是一个函数式接口的lambda实现,每次都new新对象出来。
            return productSupplier.get();
        }
        throw new IllegalArgumentException("No such product" + productEnum.name());
    }
}
三、开发中的应用简单工厂模式的例子

如果不想所创建的对象都是子类继承父类,高度耦合的(实际也很少这样),但在实际开发中又需要经常用到创建对象,可以试试下面这种方式。
这个是我在开发中所运用到的,项目需要大量调用淘宝的接口,淘宝请求参数的对象又有不同的属性,即调用淘宝线上不同的接口,都需要有不同的入参对象DTO,所以这些对象都不是继承的关系,这里用简单工厂模式来建请求淘宝接口的对象。
UML类图:


开发中应用到简单工厂模式.png
/**
 * 请求报文,T 携带简单工厂所创建的不同对象
 * @author Fiuty
 */
public class RequestDTO <T>{
    private T payload;

public class Animal {

    private String name;

    public Animal(String name) {
        this.name = name;
    }

public class People {

    private String name;

    public People(String name) {
        this.name = name;
    }
}

工厂接口类,建对象的方法,统一返回RequestDTO<T>,在泛型中返回所需的对象,而这些对象都不是子类继承父类,高度耦合。

public interface SimpleFactoryAdvanceService {

    RequestDTO<People> createPeople(String name);

    RequestDTO<Animal> createAnimal(String name);
}

工厂实现类

public class SimpleFactoryAdvanceServiceImpl implements SimpleFactoryAdvanceService {

    @Override
    public RequestDTO<People> createPeople(String name) {
        return new RequestDTO<>(new People(name));
    }

    @Override
    public RequestDTO<Animal> createAnimal(String name) {
        return new RequestDTO<>(new Animal(name));
    }
}

最后调用工厂实现类来生产我们所需的对象:

public class SimpleFactoryPatternAdvance {

    public static void main(String[] agrs) {
        SimpleFactoryAdvanceServiceImpl factory = new SimpleFactoryAdvanceServiceImpl();
        RequestDTO<Animal> animalRequest = factory.createAnimal("特朗普");
        RequestDTO<People> peopleRequest = factory.createPeople("上帝");
        System.out.println("动物的名字是:"+animalRequest.getPayload().getName());
        System.out.println("人类的名字是:"+peopleRequest.getPayload().getName());
    }
}

参考资料:
https://blog.csdn.net/LoveLion/article/details/17517213#commentBox
《Java8 实战》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容

  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,227评论 1 2
  • 第5章 引用类型(返回首页) 本章内容 使用对象 创建并操作数组 理解基本的JavaScript类型 使用基本类型...
    大学一百阅读 3,231评论 0 4
  • 注:之前关于Java8的认知一直停留在知道有哪些修改和新的API上,对Lambda的认识也是仅仅限于对匿名内部类的...
    mualex阅读 2,821评论 1 4
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,172评论 0 3
  • 一提到弟弟,我就十分恼怒。你看看他,又拿起了彩笔在墙上乱画,鞋还没脱就上床乱踩。看他整天搞破坏不说,还...
    幸福人生云阅读 685评论 2 11