四、工厂模式(Factory)

本章目录如下:

        一、阶段一

        二、阶段二

        三、阶段三

        四、java中的装饰者

        五、模式问答

        六、设计原则总结

我们不应该针对实现编程,但是每次使用new初始化时正是针对实现编程!这会造成耦合问题,因为初始化绑着具体类使代码脆弱、缺乏弹性。所以先驱们发明了工厂模式能使初始化从复杂的依赖中脱困。

本章需求是披萨店需要根据客户点的披萨名称生产不同的披萨,且总部要求所有加盟店制作流程一致,原料统一配发。变化为:创建对象时根据条件不同创建不同的对象。下面我们以代码的迭代演化过程为线索具体讲解工厂模式。

一、阶段一

        通过 if 来判断生产何种披萨。代码如下:

Pizza orderPizza(String type){

    Pizza pizza;

    if (type . equals (“cheese")) {

        pizza = new CheesePizza() ;

    }

    else if (type. equals ("greek"){

        pizza = new GreekPizza() ;

    }

    else if (type .equals ("pepperoni"){

        pizza = new PepperoniPizza() ;

    }

    pizza.prepare() ;

    pizza.bake() ;

    pizza.cut() ;

    pizza.box() ;

    return pizza;

}

        这是最常规的做法,但是缺点很明显:不能应对披萨种类的增加、减少这两种变化,且变化发生时只能修改原有代码,没有符合“开放——关闭”原则。

二、阶段二

        既然方案一不能应对变化,那么就要抽象、封装变化。下面我们通过简单工厂(静态工厂)改进程序。

        简单工厂定义:把创建对象的代码块抽象出来单独封装。

        简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。但由于经常被使用,所以我们给她一个设计模式荣誉奖。

public class SimplePizzaFactory{

    public Pizza createPizza(String type) {

        Pizza pizza = null;

        if (type. equals ("cheese")) {

            pizza = new CheesePizza() ;

        }

        else if (type.equals ("pepperoni")) {

            pizza = new PepperoniPizza() ;

        }

        else if (type .equals("clam")) {

            pizza = new ClamPizza();

        }

        else if (type .equals ("veggie")) {

            pizza = new VeggiePizza();

        }

        return pizza;

    }

}

public class PizzaStore  {

    SimplePizzaFactory factory;

    public PizzaStore (SimplePizzaFactory factory) {

        this. factory = factory;

    }

    public Pizza orderPizza(String type) {

        Pizza pizza;

        pizza = factory.createPizza(type) ;

        pizza.prepare() ;

        pizza.bake() ;

        pizza.cut() ;

        pizza.box() ;

        return pizza;

    }

}

        简单工厂(静态工厂)把变化抽象出来的好处

            1)、可以提供多处使用,避免了代码重复。

            2)、使披萨的制作过程符合“开放——关闭”原则。

        缺点:不能通过继承来改变创建方法的行为。

        简单工厂实现的披萨生产类图如下:

三、阶段三

        随着披萨店的扩张,披萨店拥有了很多不同地区的加盟店(如东北、北京、河南、广东),这些加盟店需要符合该地区口味的披萨,如果按照方案二,我们为四个地区分别创建四个简单工厂即可。但是事情往往不是这么简单的,因为为了规范管理,总店希望控制加盟店的披萨制作过程,这就不是方案二能实现的了,因为方案二中每个加盟店的拥有披萨制作过程的代码,可以修改。

        在寻找新方案之前我们需要先思考需求中的变化和不变化。变化在于不同地区的披萨要符合各地区口味,不变化在于总店想要各加盟店的制作工艺相同。==>这是思考新方案的前提。就我目前了解:继承的作用是复用,即造就相同!不同目前我了解的有组合、观察者模式、装饰模式、覆写抽象方法!

        说实话《Head First设计模式》这本书我已经看了两遍了,但是当我整理时我还是懵逼了,又忘记了什么是工厂模式。截止上一段都是昨天晚上整理的,今天还是没想起来什么是设计模式,突发灵感想到了现实中的工厂,我觉得理解现实中的工厂很有助于理解什么是工厂模式。如果把多个工厂看成一个整体,那么多个工厂肯定要遵守一套规范,但是每个工厂本身又有自己的特点;如果分析一家工厂,那么工厂中的不同产品线肯定要遵守一套规范(接口),但是每种产品线(子类)本身又有自己的特点。那么客户跟谁对接呢?当然是抽象的规范(接口)啊

        从上面这段话我将工厂模式定义为:工厂模式用来处理对象的创建,将各流水线/子工厂遵守的流程封装在抽象类中,将原料的生产以抽象方法的形式定义在抽象类中,各流水线/子工厂继承抽象类并实现生产原料的抽象方法。(为什么在各流水线/子工厂中实现生产原料的抽象方法?因为各流水线/子工厂要有自己的特色,如果没有自己的特色,那就是同一个工厂了)

        工厂模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。书中对工厂模式的定义为:工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。所谓的“决定” ,并不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定子实际创建的产品是什么。

===================Pizza=================

import java.util.ArrayList;

public abstract class Pizza {

    String name;

    String dough;

    String sauce;

    ArrayList<String> toppings = new ArrayList<String>();  


    void prepare() {

        System.out.println("Prepare " + name);

        System.out.println("Tossing dough...");

        System.out.println("Adding sauce...");

        System.out.println("Adding toppings: ");

        for (String topping : toppings) {

            System.out.println("   " + topping);

        }

    }

    void bake() {

        System.out.println("Bake for 25 minutes at 350");

    }  

    void cut() {

        System.out.println("Cut the pizza into diagonal slices");

    }   

    void box() {

        System.out.println("Place pizza in official PizzaStore box");

    } 

    public String getName() {

        return name;

    }

    public String toString() {

        StringBuffer display = new StringBuffer();

        display.append("---- " + name + " ----\n");

        display.append(dough + "\n");

        display.append(sauce + "\n");

        for (String topping : toppings) {

            display.append(topping + "\n");

        }

        return display.toString();

    }

}

===================子类 Pizza=================

public class ChicagoStylePepperoniPizza extends Pizza {

    public ChicagoStylePepperoniPizza() {

        name = "Chicago Style Pepperoni Pizza";

        dough = "Extra Thick Crust Dough";

        sauce = "Plum Tomato Sauce";  

        toppings.add("Shredded Mozzarella Cheese");

        toppings.add("Black Olives");

        toppings.add("Spinach");

        toppings.add("Eggplant");

        toppings.add("Sliced Pepperoni");

    }  

    void cut() {

        System.out.println("Cutting the pizza into square slices");

    }

}

    其他的Pizza子类参考源码,整理的有!

===================PizzaStore=================

public abstract class PizzaStore {  

    abstract Pizza createPizza(String item);  //抽象方法,子类实现


    public Pizza orderPizza(String type) {

        Pizza pizza = createPizza(type);

        System.out.println("--- Making a " + pizza.getName() + " ---");

        pizza.prepare();

        pizza.bake();

        pizza.cut();

        pizza.box();

        return pizza;

    }

}

===================子类 PizzaStore=================

public class ChicagoPizzaStore extends PizzaStore {

    Pizza createPizza(String item) {        

        if (item.equals("cheese")) {            

            return new ChicagoStyleCheesePizza();        

        } else if (item.equals("veggie")) {            

            return new ChicagoStyleVeggiePizza();        

        } else if (item.equals("clam")) {            

            return new ChicagoStyleClamPizza();        

        } else if (item.equals("pepperoni")) {            

            return new ChicagoStylePepperoniPizza();        

        } else return null;

    }

}

public class NYPizzaStore extends PizzaStore {

    Pizza createPizza(String item) {

        if (item.equals("cheese")) {

            return new NYStyleCheesePizza();

        } else if (item.equals("veggie")) {

            return new NYStyleVeggiePizza();

        } else if (item.equals("clam")) {

            return new NYStyleClamPizza();

        } else if (item.equals("pepperoni")) {

            return new NYStylePepperoniPizza();

        } else return null;

    }

}

===================顾客点餐实例=================

public class PizzaTestDrive {         

    public static void main(String[] args) {        

        PizzaStore nyStore = new NYPizzaStore();        

        PizzaStore chicagoStore = new ChicagoPizzaStore();                 

        Pizza pizza = nyStore.orderPizza("cheese");        

        System.out.println("Ethan ordered a " + pizza.getName() + "\n");                 

        pizza = chicagoStore.orderPizza("cheese");        

        System.out.println("Joel ordered a " + pizza.getName() + "\n");    

    }

}

以上代码的类图如下:

由上面代码的具体类图得出工厂模式的通用类图如下:




        注意:模式设计是针对当下环境的,所考虑的变化也符合当下环境。环境质变(比如披萨店扩张为全国连锁)一般不考虑为变化,因为环境质变一般需要经历很长一个周期,设计的初期就把预计的质变考虑进去是不明智的,不能达到投入最优化!所以阶段二已经解决了我们最上面的需求,这里是产生了新的需求!

五、模式问答

1、什么是解耦?

    我的理解是解依赖!

2、问当只有一个ConcreteCreator的时候,工厂模式有什么优点?

    尽管只有一个具体创建者,工厂模式依然很有用,因为它帮助我们将产品的“实现”从“使用” 中解耦。如果增加产品或者改变产品的实现,Creator并不会受到影响(因为Creator与任何ConcreteProduct之间都不是紧耦合)。

六、设计原则总结

设计原则1:找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化 T个T的代码混在一起。该设计原则作用: “把会变化的部分取出并封装起来,以便以后可以轻易地改动或扩充此部分,而不影响不需要变化的其他部分”。

设计原则2:针对接口编程,而不是针对实现编程。===我的理解是这个原则的使用优先级排在“设计原则1:变化原则”之后,即该原则是一个在大模式已确定需要实现具体类时针对实现类采用的原则,针对范围比较小。

设计原则3:为了交互对象之间的松耦合设计而努力。总结为“多用组合少用继承”目前我见过两种组合方式:实例作为另外实例的属性,如策略模式、装饰者模式;实例作为另外实例的集合属性的成员,如观察者模式

设计原则4(开放—关闭原则):类应该对扩展开放,对修改关闭。该原则目标是允许类容易扩展,在不修改现有代码的情况下,就可搭配新的行为。这样的设计具有弹性,可以接受新的功能来应对需求的改变。

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

推荐阅读更多精彩内容

  • 设计模式基本原则 开放-封闭原则(OCP),是说软件实体(类、模块、函数等等)应该可以拓展,但是不可修改。开-闭原...
    西山薄凉阅读 3,666评论 3 13
  • 链接:https://github.com/WiKi123/DesignPattern作者: WiKi123(gi...
    树懒啊树懒阅读 3,300评论 0 2
  • 如同乘坐公交车,你永远都不知道你坐上的这趟公交会遇到什么人,什么事,沿途经过的风景是否早已变更,时间即到即下车,既...
    再来一碗孟婆汤阅读 170评论 0 0
  • 1、标示具有保健、预防治疗疾病作用的内容 这是一类常见的违反基本条款中3.6的案例,生产企业为了吸引消费者的眼球,...
    张自芳阅读 1,452评论 2 3