全面了解工厂模式

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

工厂方法特点:

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

抽象工厂特点:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。通常我们所说的工厂模式是指工厂方法模...
    zfylin阅读 1,270评论 0 7
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 01 今天周一,又是休班的日子,今天任性的睡到了自然醒。八点半才睁开自己惺忪的睡醒。睡醒之后,第一件事情就是拿起手...
    怀瑾姑娘阅读 563评论 1 1
  • 最美助孕你要艾灸+玉灵膏 本文硬广,必须来硬的,做了妈妈任重道远,且喂哺且保养,以此文直砸做妈妈的心底。 以...
    明太溪阅读 438评论 0 0
  • 冷冷的风 丝丝的凉意 带风的雨,夹雨的风 我早已分不清 这湖上来的风 迷了眼,乱了发 惟剩一个还算完整的你 凉风有...
    青若潇潇阅读 142评论 0 1