全面了解工厂模式

  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. 抽象工厂是定义一个负责创建一组产品的接口,该接口内的每一个方法都是负责创建一个产品。

工厂方法特点:

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

抽象工厂特点:

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

你可能感兴趣的:(全面了解工厂模式)