美文网首页Java 杂谈
设计模式之工厂(Factory)

设计模式之工厂(Factory)

作者: ikonan | 来源:发表于2018-07-24 21:43 被阅读1次

    工厂模式简介

    工厂模式用于对象的创建,使得客户从具体的产品中解耦。

    简单工厂

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

    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();
            }
        }
    }
    

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

    /**
     * 简单工厂模式
     */
     int type = SimpleNoodlesFactory.TYPE_GK;
     INoodles noodles = SimpleNoodlesFactory.createNoodles(type);
     noodles.desc();
    
    输出:
    还是家里的干扣面好吃 6块一碗
    

    特点

    • 它是一个具体的类,非接口 抽象类。有一个重要的create()方法,利用if或者 switch创建产品并返回。
    • create()方法通常是静态的,所以也称之为静态工厂。

    缺点

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

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

    另一种简单工厂(反射)

    利用反射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;
        }
    }
    

    点菜时:

    //兰州拉面
    INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);
    lz.desc();
    //泡面
    INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);
    pm.desc();
    
    运行结果:
    
    这样像为了工厂而工厂
    兰州拉面 上海的好贵 家里才5 6块钱一碗
    泡面好吃 可不要贪杯
    

    另一种简单工厂利用Class.forName(clz.getName()).newInstance()
    个人觉得不好,因为这样和简单的new一个对象一样,工厂方法应该用于复杂对象的初始化

    特点

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

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

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

    多方法工厂(常用)

    使用方法二 三实现的工厂,都有一个缺点:不同的产品需要不同额外参数的时候 不支持。而且如果使用时传递的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();
        }
    }
    

    使用时:

    INoodles lz2 = MulWayNoodlesFactory.createLz();
    lz2.desc();
    INoodles gk2 = MulWayNoodlesFactory.createGk();
    gk2.desc();
    
    运行结果:
    兰州拉面 上海的好贵 家里才5 6块钱一碗
    还是家里的干扣面好吃 6块一碗
    

    这种我比较青睐,增加一个新面条,只要去增加一个static方法即可,也不修改原方法逻辑

    普通工厂

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

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

    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();
        }
    }
    

    使用时:

    NoodlesFactory factory1 = new GankouFactory();
    INoodles gk3 = factory1.create();
    gk3.desc();
    
    运行结果:
    还是家里的干扣面好吃 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();//卖可乐
        }
    }
    

    使用时:

    AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
    abstractFoodFactory1.createDrinks().prices();
    abstractFoodFactory1.createNoodles().desc();
    
    abstractFoodFactory1= new LzlmFoodFactory();
    abstractFoodFactory1.createDrinks().prices();
    abstractFoodFactory1.createNoodles().desc();
    
    运行结果:
    可乐三块五
    泡面好吃 可不要贪杯
    和我一样的穷鬼都喝水,不要钱~!
    兰州拉面 上海的好贵 家里才5 6块钱一碗
    

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

    总结

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

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

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

    相关文章

      网友评论

        本文标题:设计模式之工厂(Factory)

        本文链接:https://www.haomeiwen.com/subject/lmzymftx.html