设计模式:第四篇--工厂模式

一、简单工厂

简单工厂模式:书中提到简单工程其实并没有独立为一个“模式”,只是一个编程习惯。用的人比较多了,就总结出来了。
理解:其实这和我们现实当中的建立工厂生成是一样的。比如手机生产车间,需要组装一款手机,但是由很多型号,不同的型号配置不同的配件,不同的配件可以归类,归类其实就是归类到简单工厂里面。同类配件不同型号由一种工厂生产,比如芯片;而另一类配件由另一个工厂生产。这些配件被生产出来后送到组装车间。组装的流程是不变的,都是再组装同一个牌子的手机。仅仅是将变化的对象抽取到一个“工厂”去生产。

简单工厂

案例:PizzaStore
需求:现在有一家pizza店,由于门店扩张,每个店面招师傅做pizza的话无法保证口味,于是店主想把师傅集中起来,简单建一个pizza专门做pizza,送到门店销售。
思考:抽象一个Pizza类,定义好做pizza的方法

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 8:56.
 * <p>
 * Pizza定义了Pizza的制作工序
 */
public abstract class Pizza {
    
    public void prepare() {
        System.out.println("准备材料");
    }

    public void bake() {
        System.out.println("烤");
    }

    public void cut() {
        System.out.println("切");
    }

    public void box() {
        System.out.println("包装");
    }
    
}
public class CheesePizza extends Pizza {
    @Override
    public void prepare() {
        super.prepare();
        System.out.println("prepare cheese");
    }
}

public class ClamPizza extends Pizza {
    @Override
    public void prepare() {
        super.prepare();
        System.out.println("prepare clam");
    }
}

public class PepperoniPizza extends Pizza {
    @Override
    public void prepare() {
        super.prepare();
        System.out.println("prepare pepperoni");
    }

}
  
public class VeggiePizza extends Pizza {
    @Override
    public void prepare() {
        super.prepare();
        System.out.println("prepare vegetable");
    }

}

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:03.
 * 定义工厂
 */
public class SimplePizzaFactory {

    public Pizza createPizza(String type) {

        Pizza pizza = null;

        if ("cheese".equals(type)) {
            pizza = new CheesePizza();
        } else if ("pepperoni".equals(type)) {
            pizza = new PepperoniPizza();
        } else if ("clam".equals(type)) {
            pizza = new ClamPizza();
        } else if ("veggie".equals(type)) {
            pizza = new VeggiePizza();
        }

        return pizza;

    }
}

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:01.
 */
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;
    }
}

总结:简单工程仅仅是抽象实例化对象的过程的代码而已。参考了人类工业化过程的做法。但是这个工厂可以有很多,方便拓展。由于独立于门店,披萨的生产可以拓展,多元化生产不同的产品,门店也可以多元化经营。后续门店拓展和产品拓展的时候就很方便了。比如西面的需求

需求:现在pizza扩张门店了,在芝加哥和纽约开发开分店。但是过了一段时间发现卖不出去,经过市场调研,改良了符合当地口味的pizza。
思考:这不正好吗,我开两家工厂生产符合当地口味的不就完了,门店所有的其他订购流程都不需要变。只需要提供不同工厂生产的pizza就可以。

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:03.
 * 芝加哥工厂
 */
public class ChicagoPizzaFactory extends SimplePizzaFactory{

    @Override
    public Pizza createPizza(String type) {

        Pizza pizza = null;

        if ("cheese".equals(type)) {
            pizza = new ChicagoStyleCheesePizza();
        } else if ("pepperoni".equals(type)) {
            pizza = new ChicagoPepperoniPizza();
        } else if ("clam".equals(type)) {
            pizza = new ChicagoClamPizza();
        } else if ("veggie".equals(type)) {
            pizza = new ChicagoVeggiePizza();
        }

        return pizza;

    }
}
/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:03.
 * 纽约工厂
 */
public class NYPizzaFactory extends SimplePizzaFactory{

    @Override
    public Pizza createPizza(String type) {

        Pizza pizza = null;

        if ("cheese".equals(type)) {
            pizza = new NYStyleCheesePizza();
        } else if ("pepperoni".equals(type)) {
            pizza = new NYPepperoniPizza();
        } else if ("clam".equals(type)) {
            pizza = new NYClamPizza();
        } else if ("veggie".equals(type)) {
            pizza = new NYVeggiePizza();
        }

        return pizza;
    }
}
/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 10:23.
 */
public class ChicagoStylePizzaStore {

    SimplePizzaFactory factory;

    public ChicagoStylePizzaStore(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;
    }
}
/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 10:23.
 */
public class NYStylePizzaStore {


    SimplePizzaFactory factory;

    public NYStylePizzaStore(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;
    }

}
  
pizza
简单工厂

使用工厂制造不同风味的pizza

public class PizzaStoreDrive {

    public static void main(String[] args) {


        NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore(new NYPizzaFactory());
        nyStylePizzaStore.orderPizza("cheese");
        nyStylePizzaStore.orderPizza("clam");

        ChicagoStylePizzaStore chicagoStylePizzaStore = new ChicagoStylePizzaStore(new ChicagoPizzaFactory());
        chicagoStylePizzaStore.orderPizza("cheese");
        chicagoStylePizzaStore.orderPizza("clam");


    }
}

总结:总感觉哪里不对是吧。感觉规划好了工厂都是简单工厂的模式,生产的pizza满足需求了,但是门店是不是太灵活了店。如果能好好管理门店就好了。


二、工厂方法

工厂方法模式:通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。该模式定义了一个创建对象的接口,但是由子类决定实例化的对象。工厂方法让类把实例化的动作推辞到子类。其实就是抽象的运用了。

需求:现在pizza店需要的很厉害了,不好管理,出现了各地师傅烘焙方法和流程不一样的情况,味道变化很大。需要各地门店按照总店的烘焙方法和包装方式来。
思考:我们发现决定当地风味的只有当地的门店和送到门店的工厂,也就是要由下面的门店来决定要哪个工厂生产的pizza。同时每个门店需要按照总店的加工流程来加工送过来的pizza。这样我们把变化的部分抽象出来就好了,不变的由子类继承即可。

1、将制造pizza这个动作从订购pizza抽象出来,甚至订购pizza的方法可以配置成final

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:01.
 */
public abstract class PizzaStore {

    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) {
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {

        //工厂生产需要的pizza
        Pizza pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    /**
     * 抽象工厂方法
     *
     * @param type
     * @return
     */
    abstract Pizza createPizza(String type);
}
public class ChicagoStylePizzaStore extends PizzaStore {

    public ChicagoStylePizzaStore(SimplePizzaFactory factory) {
        super(factory);
    }

    @Override
    Pizza createPizza(String type) {
        return factory.createPizza(type);
    }
}
public class NYStylePizzaStore extends PizzaStore {
    public NYStylePizzaStore(SimplePizzaFactory factory) {
        super(factory);
    }

    @Override
    Pizza createPizza(String type) {
        return factory.createPizza(type);
    }
}
抽象工厂方法

关键在于抽象工厂方法:我们看下图关键代码对比一下,将factory.createPizza()抽象出来,可以由子类去决定由返回的pizza。本来由门店的订购流程决定的,由工厂生产pizza编程由下面门店决定用哪个工厂生产的pizza,而由总店决定订购流程。注意:这里抽象的是工厂的方法,就是原来工厂调用方法抽象出来了一层。下沉到子类。

前后对比

总结:到这里我们其实已经看到了无论式pizza店还是pizza他们是创造和被创造的关系。由于拓展的需要,引入了第三方factory协调。分离了pizza店的创造职能。仔细观察我们的拓展方式,我们要拓展很多门店,同时要拓展很多pizza。因此这两者也是平级的拓展关系。简单工厂模式是简单的分离创造者(PizzaStore)的职能,重产品(Pizza)的生产,忽略了创造者的管理。而工厂方法模式是补充了简单工厂的缺点,在创造者身上开一个接口,抽象一层接口(createPizza方法)出来,让子类去实现。达到了管理创造者的效果。

核心思想:抽象的灵活运用,从简单工厂和工厂方法,体会到了抽象代码运用的妙处,可将代码抽象出一个角色去执行这段代码达到“协调”的效果,也可以抽象一段代码到方法,下沉代码,达到“管理”的效果。这里其实我们引入了一个设计原则

依赖倒置原则:依赖抽象,而不依赖具体
所谓倒置也是从具体到抽象的抽象方式,从底层开始往上抽象,而不是从顶层开始往下抽象。


三、抽象工厂

抽象工厂模式:小编理解的其实就是提供一个接口,定义对象的家族,抽象管理起来。并运用工厂方法来决定具体对象的创建,与工厂方法不同的是,抽象工厂更偏向于“组合”,定义对象家族范围更大。

需求:现在pizza店多了,在根据门店需求制作不同地域风味的pizza的时候,原来是门店准备一下原材料即可。随着量的增多,原材料也非常多了,门店也需要吧这一部分交给“工厂”去做。
思考:原料有一系列种类,可能会有很多工厂,便于管理,抽象一个顶层工厂接口,用于定义提供原材料的接口,使用原材料工厂的pizza门店依赖顶层接口,便于管理,原材料工厂。门店依旧使用工厂方法的模式来让子类决定具体使用哪个工厂创造哪些原材料对象。

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/21 10:01.
 *
 * 顶层原材料工厂:
 *     这里定义接口其实是使用了组合的思想,使用方法来组合实例化的原材料对象,其子类实现这些方法,实例化当地的具体产品需要的的原材料
 */
public interface PizzaIngredientFactory {

    Dough createDough();

    Sauce createSauce();

    Cheese createCheese();

    Veggies[] createVeggies();

    Pepperoni createPepperoni();

    Clams createClams();
}


/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/21 10:10.
 * 
 * 具体工厂
 */
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
    @Override
    public Dough createDough() {
        return new ThinCrustDough();
    }

    @Override
    public Sauce createSauce() {
        return new MarinaraSauce();
    }

    @Override
    public Cheese createCheese() {
        return new ReggianoCheese();
    }

    @Override
    public Veggies[] createVeggies() {
        return new Veggies[]{new Garlic(), new Onion(), new Mushroom(), new RedPepper()};
    }

    @Override
    public Pepperoni createPepperoni() {
        return new SlicedPepperoni();
    }

    @Override
    public Clams createClams() {
        return new FreshClams();
    }
}
/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/21 10:31.
 * 
 * 顶层产品抽象类:pizza,依赖一系列原材料种类
 */
public abstract class Pizza {

    String name;
    Dough dough;
    Sauce sauce;
    Veggies veggies[];
    Cheese cheese;
    Pepperoni pepperoni;
    Clams clams;

    public abstract void prepare();

    public void bake(){
        System.out.println("bake for 25 minutes at 350");
    }

    public void cut() {
        System.out.println("cutting the pizza into diagonal slices");
    }

    public void box() {
        System.out.println("place pizza in official PizzaStore box");
    }

    public String getName() {
        return name;
    }

    public Pizza setName(String name) {
        this.name = name;
        return this;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Pizza{");
        sb.append("name='").append(name).append('\'');
        sb.append(", dough=").append(dough);
        sb.append(", sauce=").append(sauce);
        sb.append(", veggies=").append(Arrays.toString(veggies));
        sb.append(", cheese=").append(cheese);
        sb.append(", pepperoni=").append(pepperoni);
        sb.append(", clams=").append(clams);
        sb.append('}');
        return sb.toString();
    }
}

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/21 11:24.
 * 
 * 具体产品(product)的子接口决定使用什么工厂,并且组合哪些原材料
 */
public class ClamPizza extends Pizza {

    PizzaIngredientFactory pizzaIngredientFactory;

    public ClamPizza(PizzaIngredientFactory pizzaIngredientFactory) {
        this.pizzaIngredientFactory = pizzaIngredientFactory;
    }

    @Override
    public void prepare() {
        System.out.println("preparing " + name);
        //跟工厂要原材料
        dough = pizzaIngredientFactory.createDough();
        sauce = pizzaIngredientFactory.createSauce();
        cheese = pizzaIngredientFactory.createCheese();
    }
}
/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 9:01.
 */
public abstract class PizzaStore {
    

    public Pizza orderPizza(String type) {

        //工厂生产需要的pizza
        Pizza pizza = createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

    /**
     * 抽象工厂方法
     *
     * @param type
     * @return
     */
    abstract Pizza createPizza(String type);
}

/**
 * Project <demo-project>
 * Created by jorgezhong on 2018/9/20 10:23.
 * 注意:使用了工厂方法,不同的是,类型的判断原来是放在工厂里面做的,现在放到了门店里面族,工厂只负责组合具体对象即可
 */
public class NYStylePizzaStore extends PizzaStore {

    @Override
    Pizza createPizza(String type) {

        Pizza pizza = null;

        NYPizzaIngredientFactory factory = new NYPizzaIngredientFactory();

        if ("cheese".equals(type)) {
            pizza = new CheesePizza(factory);
            pizza.setName("New York Style Cheese Pizza");

        } else if ("pepperoni".equals(type)) {
            pizza = new PepperoniPizza(factory);
            pizza.setName("New York Style Pepperoni Pizza");

        } else if ("clam".equals(type)) {
            pizza = new ClamPizza(factory);
            pizza.setName("New York Style clam Pizza");

        } else if ("veggie".equals(type)) {
            pizza = new VeggiePizza(factory);
            pizza.setName("New York Style veggie Pizza");
        }
        return pizza;

    }
}

测试:

public class Test {

    public static void main(String[] args) {
        
        NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore();
        Pizza cheese = nyStylePizzaStore.orderPizza("cheese");
        
    }

}

结果:


备注:抽象工厂的案例省略了芝加哥具体工厂类,其他具体的pizza产品类,所有原材料类,以及芝加哥门店类。大家可根据下面画出的uml类图喝依赖关系图自行补充哦,实在太多了,因此不重要的就不贴出来了

抽象工厂关系图

备注:依赖关系比较多,图比较大,只能点开来才看的清楚啦

抽象工厂依赖图

总结:

  • 从简单工厂,到工厂方法,最后到抽象工厂,工厂诠释了封装、抽象的精髓。特别是抽象的运用。我们可以从把抽象理解成abstract关键子的阶段毕业了,抽象的理解并不仅仅是关键字那么的简单,更多的是层级的管理。
  • 简单工厂:借鉴了实际工业生产的基本管理思维,是简单的抽象,将一部分抽象出来独立于另一部分,独立出来的部分可以面向更多的角色,做更多的事情。
  • 工厂方法:诠释了抽象方法是可以,下沉代码,交由子类决定实际动作。是将代码封装到下层。
  • 抽象方法:利用面向抽象,不面向具体的设计原则,进一步抽象工厂,提升为工厂族,工厂族自然对应了产品族。而使用产品的角色则可以使用工厂族来管理被生产的产品族,抽象集合工厂方法,将具体工厂使用下沉到了多个具体使用产品的角色,以决定具体的产品对象。这里貌似有点绕。但是详细看依赖他,会发现其中的精妙。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 设计模式概述 在学习面向对象七大设计原则时需要注意以下几点:a) 高内聚、低耦合和单一职能的“冲突”实际上,这两者...
    彦帧阅读 3,733评论 0 14
  • 参考资料:菜鸟教程之设计模式 设计模式概述 设计模式(Design pattern)代表了最佳的实践,通常被有经验...
    Steven1997阅读 1,162评论 1 12
  • 设计模式汇总 一、基础知识 1. 设计模式概述 定义:设计模式(Design Pattern)是一套被反复使用、多...
    MinoyJet阅读 3,889评论 1 15
  • 文/陈曦 最近花了很长时间读美国作家莫提默·J·艾德勒和查尔斯·范多伦所著的《如何阅读一本书》,其中有一半时间都在...
    陈曦_阅读 9,443评论 134 425
  • Oracle服务扫描工具Oscanner Oracle是甲骨文公司推出的关系型数据库,适用于中大规模数据存储,如大...
    大学霸IT达人阅读 767评论 0 1