Head First(四)--工厂模式

除了使用new操作符之外,还有更多制造对象的方法。你将了解到实例化这个活动不应该总是公开地进行,也会认识到初始化经常造成“耦合”问题。

一 概述:

属于 创建型设计模式,需要生成的对象叫做 产品 ,生成对象的地方叫做 工厂
使用场景:
在任何需要生成 复杂对象的地方,都可以使用工厂方法模式。 
直接用new可以完成的不需要用工厂模式
个人理解,重点就是这个 复杂(构造函数有很多参数)和是否可以直接用new。(不理解这句话的话,看完一圈例子就理解了)
下面逐个介绍我所知道的各种工厂模式以及它们的特点,使用场景,并尽可能的找出JDK、SDK里它们的身影。

二 简单(静态)工厂:

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

public abstract class INoodles {
    /**
     * 描述每种面条啥样的
     */
    public abstract void desc();
}
先来一份兰州拉面( 具体的产品类):
public class LzNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("兰州拉面 上海的好贵 家里才5 6块钱一碗");
    }
}
程序员加班必备也要吃泡面( 具体的产品类):
public class PaoNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("泡面好吃 可不要贪杯");
    }
}
还有我最爱吃的家乡的干扣面( 具体的产品类):
public class GankouNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("还是家里的干扣面好吃 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();
        }
    }
}
简单面馆就提供三种 面条(产品),你说你要啥,他就给你啥。这里我点了一份干扣面:
/**
 * 简单工厂模式
 */
 INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);
 noodles.desc();
输出:
还是家里的干扣面好吃 6块一碗
特点
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;
    }
}
点菜时:
/**
         * 另一种简单工厂
         * 利用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();
输出:

=====另一种简单工厂利用Class.forName(clz.getName()).newInstance()======
个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化
 这样像为了工厂而工厂
兰州拉面 上海的好贵 家里才5 6块钱一碗
泡面好吃 可不要贪杯
特点
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();
    }
}
使用时:
        /**
         * 多方法静态工厂(模仿Executor类)
         */
        System.out.println("==============================模仿Executor类==============================" +
                "\n 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑");
        INoodles lz2 = MulWayNoodlesFactory.createLz();
        lz2.desc();

        INoodles gk2 = MulWayNoodlesFactory.createGk();
        gk2.desc();
输出:
==============================模仿Executor类==============================
 这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑
兰州拉面 上海的好贵 家里才5 6块钱一碗
还是家里的干扣面好吃 6块一碗
源码撑腰环节
查看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>());
    }
又如JDK想增加创建 ForkJoinPool类的方法了,只想配置 parallelism参数,便在类里增加一个如下的方法:
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
这个例子可以感受到工厂方法的魅力了吧: 方便创建 同种类型的 复杂参数 对象

五 普通工厂

普通工厂就是把简单工厂中具体的工厂类,划分成两层: 抽象工厂层+具体的工厂子类层。(一般->特殊)
面条工厂( 抽象工厂类),作用就是生产面条:
public abstract class NoodlesFactory {
    public abstract INoodles create();
}
兰州拉面工厂( 具体工厂子类):
public class LzFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new LzNoodles();
    }
}
泡面工厂 ( 具体工厂子类):
public class PaoFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new PaoNoodles();
    }
}
最爱的干扣面工厂 ( 具体工厂子类):

public class GankouFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new GankouNoodles();
    }
}
使用时:
        /**
         * 普通工厂方法:
         */
        System.out.println("===========================普通工厂方法==============================" +
                "\n 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式");
        NoodlesFactory factory1 = new GankouFactory();
        INoodles gk3 = factory1.create();
        gk3.desc();
输出:
===========================普通工厂方法==============================
 这种要多写一个类,不过更面向对象吧 = = ,实际中我更倾向于使用【模仿Executor类】的方式
还是家里的干扣面好吃 6块一碗
普通工厂与简单工厂模式的区别:
可以看出,普通工厂模式特点:不仅仅做出来的 产品要抽象, 工厂也应该需要抽象
工厂方法使一个产品类的实例化 延迟到其具体工厂子类。
工厂方法的好处就是更拥抱变化。当需求变化, 只需要增删相应的类,不需要修改已有的类
而简单工厂需要修改工厂类的create()方法,多方法静态工厂模式需要增加一个静态方法。
缺点:
引入抽象工厂层后,每次新增一个具体产品类,也要同时新增一个具体工厂类,所以我更青睐 多方法静态工厂。

六 抽象工厂:

以上介绍的工厂都是 单产品系的。抽象工厂是 多产品系(貌似也有 产品家族的说法)。
举个例子来说,每个店(工厂)不仅仅卖面条,还提供饮料卖。 
提供饮料卖,饮料是产品,先抽象一个产品类,饮料:
public abstract class IDrinks {
    /**
     * 描述每种饮料多少钱
     */
    public abstract void prices();
}
然后实现两个具体产品类: 
可乐:
public class ColaDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("可乐三块五");
    }
}
屌丝还是多喝水吧:
public class WaterDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("和我一样的穷鬼都喝水,不要钱~!");
    }
}
抽象饭店,无外乎吃喝( 抽象工厂类):
public abstract class AbstractFoodFactory {
    /**
     * 生产面条
     *
     * @return
     */
    public abstract INoodles createNoodles();

    /**
     * 生产饮料
     */
    public abstract IDrinks createDrinks();
}
兰州大酒店( 具体工厂类):
public class LzlmFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new LzNoodles();//卖兰州拉面
    }

    @Override
    public IDrinks createDrinks() {
        return new WaterDrinks();//卖水
    }
}
KFC(具体工厂类):
public class KFCFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new PaoNoodles();//KFC居然卖泡面
    }

    @Override
    public IDrinks createDrinks() {
        return new ColaDrinks();//卖可乐
    }
使用:
        /**
         * 抽象工厂方法:
         */
        System.out.println("==============================抽象方法==============================" +
                "\n 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。");
        AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

        abstractFoodFactory1= new LzlmFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();
输出:
==============================抽象方法==============================
 老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。
可乐三块五
泡面好吃 可不要贪杯
和我一样的穷鬼都喝水,不要钱~!
兰州拉面 上海的好贵 家里才5 6块钱一碗
小结:
将工厂也抽象了,在使用时,工厂和产品都是面向接口编程,OO(面向对象)的不得了。
缺点
但是将工厂也抽象后,有个显著问题,就是 类爆炸了。而且每次拓展新产品种类,例如不仅卖吃卖喝,我还想卖睡,提供床位服务,这需要修改抽象工厂类,因此所有的 具体工厂子类,都被牵连,需要 同步被修改
老实说,以我这一年的水平我体会不到抽象工厂有何巨大优势,所以在我这里我没有想到很好的使用场景。希望以后在慢慢体会吧。如有您知道,希望不吝赐教。

七 个人总结和使用场景

一句话总结工厂模式: 方便创建 同种产品类型的 复杂参数 对象
工厂模式重点就是适用于 构建同产品类型( 同一个接口 基类)的不同对象时,这些对象 new很复杂,需要 很多的参数,而这些参数中 大部分都是 固定的,so,懒惰的程序员便用工厂模式封装之。 
(如果构建某个对象很复杂,需要很多参数,但这些参数大部分都是 “不固定”的,应该使用Builder模式)
为了适应程序的扩展性,拥抱变化,便衍生出了 普通工厂、抽象工厂等模式。

转自:http://blog.csdn.net/zxt0601/article/details/52798423

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值