设计模式(一) 工厂模式 五种写法总结

来源:互联网 发布:苹果手机文档软件 编辑:程序博客网 时间:2024/05/19 23:59

转载请标明出处:
http://blog.csdn.net/zxt0601/article/details/52798423

本文出自:【张旭童的博客】

系列开篇瞎BB

设计模式相关的文章学习与总结,一直有意为之,一直又觉得时机不到。

一 是怕自己代码经验还不够,学习了也不懂,强行理解没有意义。

二 是怕自己学习了以后总结出来,万一有不对的地方,误人子弟。

而在现在的公司摸爬滚打半年后,感觉自己写代码遇到了瓶颈,想写好写优雅,却不清楚这么写究竟是自以为优雅 还是真的优雅。或对着看一些系统源码、框架源码时,不太理解他们这么写是为什么。

于是我开始了学习之路,从比较简单的工厂模式开刀,看了大概10+篇资料,发现各位大大对工厂模式的各种写法叫法不一,理解也不一,而且没有一篇是比较全的收录各种写法的。so,这也坚定了我将它总结写出来的决心,既然每个人的理解都有或多或少的缺失或冲突,那我也总结一份我的理解,呈现出来,供各位看官参考 点评。

一概述:

属于创建型设计模式,需要生成的对象叫做产品 ,生成对象的地方叫做工厂

使用场景:

在任何需要生成复杂对象的地方,都可以使用工厂方法模式。
直接用new可以完成的不需要用工厂模式

个人理解,重点就是这个复杂 (构造函数有很多参数)和 是否可以 直接用new。(不理解这句话的话,看完一圈例子就理解了)

下面逐个介绍我所知道的各种工厂模式以及它们的特点,使用场景,并尽可能的找出JDK SDK里它们的身影。


二 简单(静态)工厂:

一个栗子:
我喜欢吃面条,抽象一个面条基类,(接口也可以),这是产品的抽象类

public abstract class INoodles {    /**     * 描述每种面条啥样的     */    public abstract void desc();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

先来一份兰州拉面(具体的产品类):

public class LzNoodles extends INoodles {    @Override    public void desc() {        System.out.println("兰州拉面 上海的好贵 家里才5 6块钱一碗");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

程序员加班必备也要吃泡面(具体的产品类):

public class PaoNoodles extends INoodles {    @Override    public void desc() {        System.out.println("泡面好吃 可不要贪杯");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

还有我最爱吃的家乡的干扣面(具体的产品类):

public class GankouNoodles extends INoodles {    @Override    public void desc() {        System.out.println("还是家里的干扣面好吃 6块一碗");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

准备工作做完了,我们来到一家“简单面馆”(简单工厂类),菜单如下:

public class SimpleNoodlesFactory {    public static final int TYPE_LZ = 1;//兰州拉面    public static final int TYPE_PM = 2;//泡面    public static final int TYPE_GK = 3;//干扣面    public static INoodles createNoodles(int type) {        switch (type) {            case TYPE_LZ:                return new LzNoodles();            case TYPE_PM:                return new PaoNoodles();            case TYPE_GK:            default:                return new GankouNoodles();        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

简单面馆就提供三种面条(产品),你说你要啥,他就给你啥。这里我点了一份干扣面:

/** * 简单工厂模式 */ INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK); noodles.desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

输出:

还是家里的干扣面好吃 6块一碗
  • 1
  • 1

特点

1 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。

2 create()方法通常是静态的,所以也称之为静态工厂

缺点

1 扩展性差(我想增加一种面条,除了新增一个面条产品类,还需要修改工厂类方法)

2 不同的产品需要不同额外参数的时候 不支持。

三 另一种简单工厂(反射):

利用反射Class.forName(clz.getName()).newInstance()实现的简单工厂:

public class StaticNoodlesFactory {    /**     * 传入Class实例化面条产品类     *     * @param clz     * @param <T>     * @return     */    public static <T extends INoodles> T createNoodles(Class<T> clz) {        T result = null;        try {            result = (T) Class.forName(clz.getName()).newInstance();        } catch (Exception e) {            e.printStackTrace();        }        return result;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

点菜时:

        /**         * 另一种简单工厂         * 利用Class.forName(clz.getName()).newInstance()         */        System.out.println("=====另一种简单工厂利用Class.forName(clz.getName()).newInstance()======" +                "\n个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化" +                "\n 这样像为了工厂而工厂");        //兰州拉面        INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);        lz.desc();        //泡面        INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);        pm.desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

输出:

=====另一种简单工厂利用Class.forName(clz.getName()).newInstance()======个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化 这样像为了工厂而工厂兰州拉面 上海的好贵 家里才5 6块钱一碗泡面好吃 可不要贪杯
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

特点

1 它也是一个具体的类,非接口 抽象类。但它的create()方法,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改create()的代码

缺点

这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题:

1 个人觉得不好,因为Class.forName(clz.getName()).newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。

2 不同的产品需要不同额外参数的时候 不支持。

四 多方法工厂(常用)

使用方法二 三实现的工厂,都有一个缺点:不同的产品需要不同额外参数的时候 不支持。

而且如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。

而多方法的工厂模式为不同产品,提供不同的生产方法,使用时 需要哪种产品就调用该种产品的方法,使用方便、容错率高

工厂如下:

public class MulWayNoodlesFactory {    /**     * 模仿Executors 类     * 生产泡面     *     * @return     */    public static INoodles createPm() {        return new PaoNoodles();    }    /**     * 模仿Executors 类     * 生产兰州拉面     *     * @return     */    public static INoodles createLz() {        return new LzNoodles();    }    /**     * 模仿Executors 类     * 生产干扣面     *     * @return     */    public static INoodles createGk() {        return new GankouNoodles();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

使用时:

        /**         * 多方法静态工厂(模仿Executor类)         */        System.out.println("==============================模仿Executor类==============================" +                "\n 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑");        INoodles lz2 = MulWayNoodlesFactory.createLz();        lz2.desc();        INoodles gk2 = MulWayNoodlesFactory.createGk();        gk2.desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

输出:

==============================模仿Executor类============================== 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑兰州拉面 上海的好贵 家里才5 6块钱一碗还是家里的干扣面好吃 6块一碗
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

源码撑腰环节

查看Java源码:java.util.concurrent.Executors类便是一个生成Executor 的工厂 ,其采用的便是 多方法静态工厂模式

例如ThreadPoolExecutor类构造方法有5个参数,其中三个参数写法固定,前两个参数可配置,如下写。

    public static ExecutorService newFixedThreadPool(int nThreads) {        return new ThreadPoolExecutor(nThreads, nThreads,                                      0L, TimeUnit.MILLISECONDS,                                      new LinkedBlockingQueue<Runnable>());    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

又如JDK想增加创建ForkJoinPool类的方法了,只想配置parallelism参数,便在类里增加一个如下的方法:

    public static ExecutorService newWorkStealingPool(int parallelism) {        return new ForkJoinPool            (parallelism,             ForkJoinPool.defaultForkJoinWorkerThreadFactory,             null, true);    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这个例子可以感受到工厂方法的魅力了吧:方便创建 同种类型的 复杂参数 对象

五 普通工厂

普通工厂就是把简单工厂中具体的工厂类,划分成两层:抽象工厂层+具体的工厂子类层。(一般->特殊)

面条工厂(抽象工厂类),作用就是生产面条:

public abstract class NoodlesFactory {    public abstract INoodles create();}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

兰州拉面工厂 (具体工厂子类):

public class LzFactory extends NoodlesFactory {    @Override    public INoodles create() {        return new LzNoodles();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

泡面工厂 (具体工厂子类):

public class PaoFactory extends NoodlesFactory {    @Override    public INoodles create() {        return new PaoNoodles();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

最爱的干扣面工厂 (具体工厂子类):

public class GankouFactory extends NoodlesFactory {    @Override    public INoodles create() {        return new GankouNoodles();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用时:

        /**         * 普通工厂方法:         */        System.out.println("===========================普通工厂方法==============================" +                "\n 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式");        NoodlesFactory factory1 = new GankouFactory();        INoodles gk3 = factory1.create();        gk3.desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

输出:

===========================普通工厂方法============================== 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式还是家里的干扣面好吃 6块一碗
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

普通工厂与简单工厂模式的区别:

可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象

工厂方法使一个产品类的实例化延迟到其具体工厂子类.

工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改已有的类

而简单工厂需要修改工厂类的create()方法,多方法静态工厂模式需要增加一个静态方法。

缺点:

引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐 多方法静态工厂。

六 抽象工厂:

以上介绍的工厂都是单产品系的。抽象工厂是多产品系 (貌似也有产品家族的说法)。

举个例子来说,每个店(工厂)不仅仅卖面条,还提供饮料卖。
提供饮料卖,饮料是产品,先抽象一个产品类,饮料:

public abstract class IDrinks {    /**     * 描述每种饮料多少钱     */    public abstract void prices();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

然后实现两个具体产品类:
可乐:

public class ColaDrinks extends IDrinks {    @Override    public void prices() {        System.out.println("可乐三块五");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

屌丝还是多喝水吧:

public class WaterDrinks extends IDrinks {    @Override    public void prices() {        System.out.println("和我一样的穷鬼都喝水,不要钱~!");    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

抽象饭店,无外乎吃喝(抽象工厂类):

public abstract class AbstractFoodFactory {    /**     * 生产面条     *     * @return     */    public abstract INoodles createNoodles();    /**     * 生产饮料     */    public abstract IDrinks createDrinks();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

兰州大酒店(具体工厂类):

public class LzlmFoodFactory extends AbstractFoodFactory {    @Override    public INoodles createNoodles() {        return new LzNoodles();//卖兰州拉面    }    @Override    public IDrinks createDrinks() {        return new WaterDrinks();//卖水    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

KFC(具体工厂类):

public class KFCFoodFactory extends AbstractFoodFactory {    @Override    public INoodles createNoodles() {        return new PaoNoodles();//KFC居然卖泡面    }    @Override    public IDrinks createDrinks() {        return new ColaDrinks();//卖可乐    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用:

        /**         * 抽象工厂方法:         */        System.out.println("==============================抽象方法==============================" +                "\n 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。");        AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();        abstractFoodFactory1.createDrinks().prices();        abstractFoodFactory1.createNoodles().desc();        abstractFoodFactory1= new LzlmFoodFactory();        abstractFoodFactory1.createDrinks().prices();        abstractFoodFactory1.createNoodles().desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出:

==============================抽象方法============================== 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。可乐三块五泡面好吃 可不要贪杯和我一样的穷鬼都喝水,不要钱~!兰州拉面 上海的好贵 家里才5 6块钱一碗
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

小结:

将工厂也抽象了,在使用时,工厂和产品都是面向接口编程,OO(面向对象)的不得了。

缺点

但是将工厂也抽象后,有个显著问题,就是类爆炸了。而且每次拓展新产品种类,例如不仅卖吃卖喝,我还想卖睡,提供床位服务,这需要修改抽象工厂类,因此所有的具体工厂子类,都被牵连,需要同步被修改

老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。如有您知道,希望不吝赐教。


七 个人总结和使用场景

一句话总结工厂模式:方便创建 同种产品类型的 复杂参数 对象

工厂模式重点就是适用于 构建同产品类型(同一个接口 基类)的不同对象时,这些对象new很复杂,需要很多的参数,而这些参数中大部分都是固定的,so,懒惰的程序员便用工厂模式封装之。
(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是“不固定”的,应该使用Builder模式)

为了适应程序的扩展性,拥抱变化,便衍生出了 普通工厂、抽象工厂等模式。

代码传送门:
github:
https://github.com/mcxtzhang/Demos/tree/master/libfactorypattern

学习参考:

http://blog.csdn.net/zhangerqing/article/details/8194653
http://blog.csdn.net/column/details/code-design.html
Android源码设计模式书籍
Java设计模式深入研究书籍
一些搜 工厂模式 出来的文章….