美文网首页
全面了解工厂模式

全面了解工厂模式

作者: 免费的午餐 | 来源:发表于2019-08-05 17:49 被阅读0次
    1. 工厂模式是啥?
    2. 简单工厂是工厂模式吗?
    3. 工厂模式的哼哈二将(工厂方法、抽象工厂)

    一、工厂模式是啥?

    ​ 我们知道在java中创建对象是需要使用new操作符,但有时候我们显示的进行实例化的时候就会经常造成初始化“耦合”的问题。

    看下列代码有什么问题?

        Fruit fruit;
            if (apple){
                //苹果
                fruit = new Apple();
            }else if (pear){
                //梨
                fruit = new Pear();
            }else if (peach){
                //桃
                fruit = new Peach();
            }
    

    上面,代码我们可以看出 当我们创建具体水果类的时候,通常会像上面这种方式书写代码。究竟要实例化哪一个类,要在运行时根据条件决定。

    当我们看到这样的代码,一旦有变化或者扩展,比如新增一个葡萄类就必须重新打开这段代码进行修改检查。通常这样修改过的代码将造成部分系统难维护和更新,而且容易犯错。

    当我们新增或删除新的类时,需要直接在上述代码中修改,那么我们的代码并非“对修改关闭,对扩展开发”。

    那我们该怎么办呢,根据OO设计原则,找出变化的方面,把它们从不变的部分中分离出来。

    识别变的方面

    ​ 假设我们有一个煎饼店,我们的代码可能会这么写:

        //订单处理
    JianBing orderJB(){
            JianBing jb = new JianBing();
            
            //准备
            jb.prepare();
            //摊煎饼
            jb.bake();
            //切
            jb.cute();
            //装袋
            jb.bag();
            
            return jb;
        }
    

    如果我们在做一些不同类型的煎饼,看代码这么写:

     JianBing orderJB(String type){
            JianBing jb = new JianBing();
    
        //这段代码根据不同的类型,制作不同的煎饼,会根据不同地方人的口味会经常性的修改
            //经常发生变化的部分
            if(type.equals("huotui")){
                //火腿
                pancake = new HuoTuiJianBing();
            }else if(type.equals("latiao")){
                //辣条
                pancake = new LatiaoJianBing();
            }else if(type.equals("jidan")){
                //鸡蛋
                pancake = new JiDanJianBing();
            }
    
       
       //制作流程一般不会发生变化
            jb.prepare();
            jb.bake();
            jb.cute();
            jb.bag();
    
            return jb;
        }
    

    我们可以看出上述代码并没有对修改关闭,如果煎饼店修改煎饼的不同风味的话,就必须对上面代码中的if语句进行修改,增加或删除对象,可以看出创建不同类型的煎饼的随着时间的推移和市场的变化,会经常的变化,代码也会被一改再改。

    封装创建对象的代码

    我们知道了下面代码是经常变化变化的部分:

            //经常发生变化的部分
            if(type.equals("huotui")){
                //火腿
                pancake = new HuoTuiJianBing();
            }else if(type.equals("latiao")){
                //辣条
                pancake = new LatiaoJianBing();
            }else if(type.equals("jidan")){
                //鸡蛋
                pancake = new JiDanJianBing();
            }
    

    我们就抽取,这段变化的代码,放到一个对象当中,让一个对象专门负责创建煎饼实例,如果任何对象想要创建煎饼那么就找这个对象就行了。我们称这个新对象为"工厂"。

    二、简单工厂

    ​ 我们定义一个类,为煎饼封装创建对象的代码。代码如下:

    public class SimpleJianBingFactory {
        //只负责创建具体实例
        JianBing createJB(String type){
            JianBing jb = null;
            if(type.equals("huotui")){
                pancake = new HuoTuiJianBing();
            }else if(type.equals("latiao")){
                pancake = new LatiaoJianBing();
            }else if(type.equals("jidan")){
                pancake = new JiDanJianBing();
            }
            return jb;
        }
    }
    

    SimpleJianBingFactory是我们新创的类,他只负责一件事,就是帮助创建不同的具体实例对象。

    ​ 现在我们可以用工厂来为我们创建煎饼,我们煎饼店要做的改变是:

    public class JianBingStore {
        SimplePizzaFactory factory;
    
        //我们必须要先有一个 工厂给我们创建煎饼实例
        public JianBingStore(SimplePizzaFactory factory) {
            this.factory = factory;
        }
    
        JianBing orderJB(String type){
            //这里我们把new对象 变成了让工厂类返回一个对象,这里不再进行实例化了
            JianBing jb = factory.createPizza(type);
    
            jb.prepare();
            jb.bake();
            jb.cute();
            jb.bag();
    
            return jb;
        }
    }
    

    SimpleJianBingFactory就是我们经常说的简单工,简单工厂其实并不是一种设计模式,反而更像是一种编码习惯。有些开发人员经常把这个编程习惯误认为"工厂模式"。

    小贴士:

    设计模式中,所谓的"实现一个接口" 并 "不一定" 表示 "写一个类利用implement来实现某个java接口"。

    "实现一个接口" 泛指 "实现某个超类型(可以是类或接口)的某个方法"。

    三、工厂模式哼哈二将:工厂方法、抽象工厂

    (一)工厂方法

    ​ 现在我们想多开几家煎饼店,我们要做北京和上海各开一家店,因为每个地方的人的口味不同,所以,开的连锁店也做的产品也有些不同。

    ​ 首先,看看JianBingStore要做的改变:

    public abstract class JianBingStore {
        
      //订单处理的流程不变
        JianBing orderJB(String type){
           JianBing jb = createJB(type);
    
            jb.prepare();
            jb.bake();
            jb.cute();
            jb.bag();
    
            return jb;
        }
        
        //我们把工厂创建的对象的方式,改成了一个抽象发方法来创建对象
        abstract JianBing createJB(String type);
    }
    

    ​ 现在已经有一个JianBingStore作为超类;让每个其他地方的点继承该超类,每个子类自行决定做什么类型的煎饼。

    现在来创建煎饼店:

    //上海店
    public class JianBingSHStore extends JianBingStore {
        @Override
        JianBing createPizza(String type) {
            //这里创建的具体实例,都是上海风味的
            if (type.equals("huotui")){//火腿
                return new SHHuotuiJianBing();
            }else if (type.equals("jidan")){//鸡蛋
                return new SHJidanJianBing();
            }else if (type.equals("jiliu")){//鸡柳
                return new SHJiliuJianBing();
            }
            return null;
        }
    }
    
    //广州店
    public class JianBingGZStore extends JianBingStore {
        @Override
        JianBing createPizza(String type) {
            //这里创建的具体实例,都是深圳风味的
            if (type.equals("latiao")){//辣条
                return new GZLatiaoJianBing();
            }else if (type.equals("jidan")){//鸡蛋
                return new GZJidanJianBing();
            }else if (type.equals("jiliu")){//鸡柳
                return new GZJiliuJianBing();
            }
            return null;
        }
    }
    

    请注意,超类的orderJB并不知道真正创建的煎饼是哪一种,他只知道这个煎饼可以被准备、烤、切、装袋。

    原本是有一个对象负责所有具体的实例化,现在通过对JianBingStore做的一些改变,变成了让一群子类来负责实例化。而超类中的抽象方法createPizza(String type) 就相当于一个工厂一样,用来产生具体的实例,那么这个抽象方法就是叫工厂方法。

    工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。

    //1. 工厂方法是抽象的,依赖子类来创建对象
    //2. 工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值
    //3. 工厂方法将客户(也就是超类中的代码,例如orderJB())和时间创建具体产品的代码进行分隔出来
    //4. 工厂方法可以需要参数也可能不需要 来指定产品
    abstract Product factoryMethod(String type)
    

    工厂模式的定义:

    ​ 定义一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类

    ​ 注:工厂方法和超类是否总是抽象的?

    ​ 不,可以定义一个默认的工厂方法来产生具体的产品,这么一来,即使创建者(超类)没有任何子类也可以创建产品。

    (二)抽象工厂

    ​ 为了保证在各地开的分店的原材料能够保质,我们可以创建一个工厂,专门负责生产原材料 供给各个店铺。因为不同地方的店需要的原材料不完全一样,我们不能在北京创建一家工厂来生成原材料,然后在送往上海、广州等地,这样很不划算。

    ​ 现在,我们先建造一个家工厂生成原材料;这个工厂将负责创建所有店需要的原材料。

    ​ 我们先定义一个接口,这个接口负责创建所有原材料:

    /**
     * 原材料工厂 负责创建所有的原材料
     */
    public interface JianBingIngredientFactory {
    
        //面团
        public Dough createDough();
    
        //酱料
        public Sauce createSauce();
    
        //蔬菜
        public Veggies[] createVeggies();
    
        //肉类
        public Meat[] createMeat();
    }
    
    

    建好原料工厂后,为每个区域的店建造一个工厂。我们需要创建一个JianBingIngredientFactory子类来实现每一个创建方法。根据不通过的地方,我们具体实现的细节是不一样的。

    //上海原料厂 供上海
    public class SHJianBingIngredientFactory implements JianBingIngredientFactory {
        @Override
        public Dough createDough() {
            return new SHDough();
        }
    
        @Override
        public Sauce createSauce() {
            return new SHSauce();
        }
    
        @Override
        public Veggies[] createVeggies() {
            return new Veggies[] = {new Garlic(),new Onion()};
        }
    
        @Override
        public Meat[] createMeat() {
            return new Meat[] = {new JLW(),new WZW()};
        }
    }
    
    //广州原材料厂 类似
    

    我们重新开始做煎饼,使用工厂原料。先从抽象的JianBing类开始:

    public  abstract class JianBing {
    
        String name;
        String dough;//面团
        String sauce;//酱料
        Veggies veggies[];//蔬菜
        Meat meat[];//肉类
    
    
        /*
        准备 准备各种原材料
         */
        abstract void prepare();
    
        /**
         * 烘烤
         */
         void bake(){
             System.out.printf("烘烤2分钟");
         }
    
        /**
         * 切片
         */
         void cut(){
             System.out.printf("切片");
         }
    
        /**
         * 装袋
         */
         void bag(){
             System.out.printf("装袋");
         }
    
        public String getName() {
            return name;
        }
    }
    
    

    创建一个上海煎饼类:

    //创建一个 上海的火腿煎饼
    public class SHHuotuiJianBing extends JianBing {
        SHJianBingIngredientFactory factory;
    
        public SHHuotuiJianBing(SHJianBingIngredientFactory factory) {
            this.factory = factory;
        }
    
        @Override
        void prepare() {
            //从上海原料工厂 获取原料
            dough = factory.createDough();
            sauce = factory.createSauce();
            meat = factory.createMeat();
        }
    }
    

    上海煎饼店也发送了些许改变:

    //上海店
    public class JianBingSHStore extends JianBingStore {
      
      
        @Override
        JianBing createJB(String type) {
          //上海原材料工厂
          JianBingIngredientFactory factory =  new SHJianBingIngredientFactory();
          
            //这里创建的具体实例,都是上海风味的
            if (type.equals("huotui")){//火腿
                //接收一个原料工厂
                return new SHHuotuiJianBing(factory);
            }else if (type.equals("jidan")){//鸡蛋
                return new SHJidanJianBing(factory);
            }else if (type.equals("jiliu")){//鸡柳
                return new SHJiliuJianBing(factory);
            }
            return null;
        }
    }
    

    这样当我们下单时,就会执行以下流程:

    1. 首先我们创建一个上海煎饼店 JianBingStore store = new SHJinBingStore()

    2. store.orderJB("huotui") 接收订单

    3. store.orderJB("huotui")会调用createPizza()方法

    4. 当createPizza()被调用时,也就开始涉及原料工厂了

    5. 一但调用了prepare()方法,那么工厂就要准备好原料了

    6. 最后被制作出来了打包

    ​ 抽象工厂的定义:

    ​ 提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

    抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道和关心实际产出的产品是什么。

    抽象工厂的方法经常以工厂方法的方法来实现,抽象工厂的任务是定义一个负责创建一组产品的接口。这个接口内的每个方法都负责创建一个具体的产品。

    总结:

    1. 所有的工厂模式都是用来封装对象的创建的。

    2. 工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建过程进行封装的目的。

    3. 抽象工厂是定义一个负责创建一组产品的接口,该接口内的每一个方法都是负责创建一个产品。

    工厂方法特点:

    ​ 工厂方法可以吧客户代码从需要实例化的具体类中解耦。或者如果目前还不知道将来要实例化那些具体类时,也可以使用,使用方法很简单,只要把该类继承并实现里面的工厂方法就可以了。

    抽象工厂特点:

    ​ 当需要创建一组产品可以使用抽象工厂。利用抽象工厂 需要扩展一个类,因为抽象工厂需要定义一个接口

    相关文章

      网友评论

          本文标题:全面了解工厂模式

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