每次写博客都不知道要怎么引入要写的主题,挺头疼的一件事。今天就直接开门见山,今天要学的就是工厂模式,工厂就是批量生产制造东西的地方。在这里,工厂就是批量生产对象的地方。

学习书上的例子

假如你现在有一个披萨店PizzaStore,店里有各种各样的pizza,现在你需要根据订单生产pizza,可能会这样做

    public Pizza orderPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese"))
pizza = new CheesePizza();
else
pizza = new GreekPizza();
}

将具体需要哪种pizza直接放在orderPizza方法中,那需要增加新的pizza呢……好吧,这里先跳过,先看一下如果使用简单工厂要怎么做。(书上说简单工厂不算一个“真正的”模式,更像一种编程习惯,好吧,这个我们就不纠结了)需要一个专门生产pizza的类SimplePizzaFactory,把生产pizza的任务就交给它了,就像这样:

/**
* 专门生产pizza的工厂
*/
public class SimplePizzaFactory {
public static Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese"))
pizza = new CheesePizza();
else
pizza = new GreekPizza();
return pizza;
}
}

好了,得到了想要的pizza,现在接着完成pizza的订单,还需要这样的几个步骤:准备prepare(), 烘烤bake(), 切片cut(), 装盒box(). 这几个方法对所有pizza都是一样的, 所以需要一个pizza的抽象类,这几个方法可以直接在这个抽象类中实现

public abstract class Pizza {
public void prepare() {
System.out.println("prepare");
description();
} public void bake() {
System.out.println("bake");
}
public void cut() {
System.out.println("cut");
}
public void box() {
System.out.println("box");
}
public abstract void description();//描述pizza
}

具体的pizza(如cheesePizza)继承这个Pizza这个类,在description方法中添加自己的描述

public class CheesePizza extends Pizza {
@Override
public void description() {
System.out.println("default NY Cheese Pizza");
} }

其他的我就不写了,现在可以用工厂生产pizza了,这时候orderPizza方法大概就是这样的

public class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = SimplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}

你可能会问,这不就是把问题从一个地方搬到了另一个地方吗,问题依然存在啊。是的,我开始也是这样想的,但是仔细想想,这样还是有好处的,如果说其他的类中也需要生产pizza呢?难道你还要在把生产pizza的代码写一遍?用书上表达意思就是简单工厂SimplePizzaFactory的客户可能还有其他的,就是说在其他的类中也需要生产pizza。所以说需要依据具体情况创建对象时简单工厂是一个好的选择。

书上把工厂模式分成了3类(我这里把简单工厂也当作一类),分别是简单工厂,工厂方法和抽象工厂,简单工厂上面已经介绍了,然后看一下工厂方法和抽象工厂

工厂方法

现在pizza店的生意比较好,准备开加盟店了,在纽约和芝加哥等地方开一些加盟店,就有了NYPizzaStore和ChicagoPizzaStore等,可是由于地域差异造成了人们的口味也不同,就是说同一种pizza的风味都不一样,比如纽约的cheesePizza的饼要厚一些,而芝加哥的cheesePizza的饼要薄一些。这样就不能使用简单工厂呢?先不作回答,接着往下看,由于各个地方的pizza的风味不一样,所以让他们自己制造自己的pizza,就在PizzaStore中写一个抽象的制作pizza的方法

public abstract Pizza createPizza(String type);

然后在orderPizza中调用这个方法就可以得到想要的pizza,原来的简单工厂就可以替换为

Pizza pizza = createPizza(type);

具体的pizza店(如NYPizzaStore和ChiragoPizzaStore)继承自抽象的pizza店PizzaStore,然后实现各自制作pizza的方法createPizza,比如像这样

NYPizzaStore

    //默认为纽约风味的pizza
@Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese"))
pizza = new CheesePizza();
else
pizza = new GreekPizza();
return pizza;
}

ChiragoPizzaStore

    @Override
public Pizza createPizza(String type) {
Pizza pizza = null;
if (type.equals("cheese")) {
pizza = new ChicagoCheesePizza();
}
else
pizza = new ChicagoGreekPizza();
return pizza;
}

可以测试一下

        ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();
chicagoPizzaStore.orderPizza("cheese");

可以看到,不同pizza店生产不同的pizza,都是依靠public abstract Pizza createPizza(String type);这个方法,能够生产不同的pizza,就像一个工厂一样。所以把这个方法称为工厂方法。工厂方法都是要返回一个具体的产品的。

工厂方法模式的定义

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

还没有完,可以看到,2个具体的pizza店都是负责创建pizza的,于是我就想能不能把这2个pizza店创建pizza的动作交给一个简单工厂来处理,就像这样

public class NoSimplePizzaFactory {
//通过pizza店clazz来区分地域
public static Pizza createPizza(Class<? extends PizzaStore> clazz, String type) {
if (clazz == null)
throw new NullPointerException("address不能为空");
if (StringUtils.isNullOrEmpty(type))
throw new NullPointerException("type不能为空"); if (clazz.getSimpleName().equals(NYPizzaStore.class.getSimpleName())) {
if (type.equals("cheese"))
return new CheesePizza();
else
return new GreekPizza();
} else if (clazz.getSimpleName().equals(ChicagoPizzaStore.class.getSimpleName())) {
if (type.equals("cheese"))
return new ChicagoCheesePizza();
else
return new ChicagoGreekPizza();
} else
return null;
}
}

于是在具体的pizza店中调用制作pizza的方法就可以得到pizza了

NoSimplePizzaFactory.createPizza(getClass(), type);

再来看一下刚才提出的问题:能不能在抽象类PizzaStore中使用简单工厂来制作各地区不同的pizza?答案是可以的,因为我们现在有了一个简单工厂NoSimplePizzaFactory(简单工厂不简单啊),它可以生产各地区不同的pizza。所以来一个重载的orderPizza方法或把原来的orderPizza方法修改一下,就像这样

    public Pizza orderPizza(Class<? extends PizzaStore> clazz, String type) {
Pizza pizza = NoSimplePizzaFactory.createPizza(clazz, type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}

这样就可以制作不同地区不同的pizza了。

现在pizza店的生意越来越好了,随机而来的问题是制作pizza的原料供不应求,而且各个地区的pizza所用的原料还可能不同,如图

为了解决原料供不应求和各地制作pizza所用原料不同的问题,我们可以在每个地区都建一座原料工厂。来供应本地区pizza店所需要的原料。新建一个抽象的原料工厂PizzaIngredientFactory,里面有制造原料的方法,比如:

    Dough createDough();
Sauce createSauce();

这个工厂是一个接口,然后2个具体的原料工厂ChicagoPizzaIngredientFactory和NYPizaaIngredientFactory实现它,不同的工厂制作不同的原料,

ChicagoPizzaIngredientFactory

    @Override
public Dough createDough() {
return new ChicagoDough();
} @Override
public Sauce createSauce() {
return new ChicagoSauce();
}

NYPizaaIngredientFactory

    @Override
public Dough createDough() {
return new NYDough();
} @Override
public Sauce createSauce() {
return new NYSauce();
}

现在在pizza中加入这些原料,也就是在Pizza类中加入原料属性

    Dough dough;    //面团
Sauce sauce; //酱料

大家还可以自己添加其他的原料属性,这些原料是工厂生产的,所以把工厂也放在Pizza类中

    private PizzaIngredientFactory ingredientFactory;
public Pizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}

然后在具体的pizza中添加对应的构造方法,然后在准备prepare阶段生产原料,所以prepare方法修改一下

    public void prepare() {
System.out.println("prepare");
description();
if (ingredientFactory != null) {
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
System.out.println(dough.getName());
System.out.println(sauce.getName());
}
}

然后在简单工厂NoSimplePizzaFactory中就可以使用原料工厂了,然后在该类中引入原料工厂

    private static PizzaIngredientFactory chicagoPizzaIngredientFactory = new ChicagoPizzaIngredientFactory();
private static PizzaIngredientFactory nyPizaaIngredientFactory = new NYPizaaIngredientFactory();

然后创建pizza是注入工厂,修改其中的即可

        if (clazz.getSimpleName().equals(NYPizzaStore.class.getSimpleName())) {
if (type.equals("cheese"))
return new CheesePizza();
else
return new GreekPizza(nyPizaaIngredientFactory);

测试一下,你会发现可以通过工厂获得不同的pizza。本例只为说明工厂模式,所以还有不足之处,例如,一旦确定了pizza的区域和种类,通过orderPizza方法得到的pizza就是确定的,并不能在动态的改变制作pizza的原料,我的思路:只要控制原料工厂就可以在制作pizza时动态的改变原料。

从本例可以看到,pizza原料工厂就是一个抽象工厂

抽象工厂模式的定义

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

工厂方法是不是潜伏在抽象工厂里面

可以看到PizzaIngredientFactory接口中有2个制造原料的抽象方法,这2个方法的作用是生产原料,所以这2个是工厂方法。

小结

简单工厂专职创建对象

工厂方法是一个生产产品的抽象方法,具体生产什么产品由具体的子类决定

抽象工厂用于制造产品的“家族”,具体产品家族由具体工厂决定,抽象工厂里的方法通常是工厂方法

读headFirst设计模式 - 工厂模式的更多相关文章

  1. 读headFirst设计模式 - 策略模式

    有些人已经解决你的问题了 什么是设计模式?我们为什么要使用设计模式?怎样使用?按照书上的说法和我自己的理解,我认为是这样的:我们遇到的问题其他开发人员也遇到过,他们利用他们的智慧和经验将问题解决了,把 ...

  2. .NET设计模式: 工厂模式

    .NET设计模式: 工厂模式(转) 转自:http://www.cnblogs.com/bit-sand/archive/2008/01/25/1053207.html   .NET设计模式(1): ...

  3. 【设计模式】Java设计模式 -工厂模式

    [设计模式]Java设计模式 -工厂模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长! 目 ...

  4. 10.Java设计模式 工厂模式,单例模式

    Java 之工厂方法和抽象工厂模式 1. 概念 工厂方法:一抽象产品类派生出多个具体产品类:一抽象工厂类派生出多个具体工厂类:每个具体工厂类只能创建一个具体产品类的实例. 即定义一个创建对象的接口(即 ...

  5. [Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  6. javascript 设计模式-----工厂模式

    所谓的工厂模式,顾名思义就是成批量地生产模式.它的核心作用也是和现实中的工厂一样利用重复的代码最大化地产生效益.在javascript中,它常常用来生产许许多多相同的实例对象,在代码上做到最大的利用. ...

  7. JavaScript设计模式——工厂模式

    工厂模式:是一种实现“工厂”概念的面上对象设计模式.实质是定义一个创建对象的接口,但是让实现这个接口的类来决定实例化哪个类.工厂方法让类的实例化推迟到子类中进行.创建一个对象常常需要复杂的过程,所以不 ...

  8. 学习:java设计模式—工厂模式

    一.工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的. 工厂模式在<Java与模式>中分为三类: 1)简单工厂模式(Simple Facto ...

  9. 设计模式——工厂模式 (C++实现)

    软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径.设计模式中运用了面向对象编程语言的重要特性:封装.继承.多态,真正领悟设计模式的精髓是可能一个漫长的过程,需要大量实践经验的积累. ...

随机推荐

  1. dm642的视频口输出

    void VP1_EDMA(int displayMode,unsigned int w,unsigned int h) {      unsigned int i=0,k=0;  EDMA_Hand ...

  2. Flex中通过RadioButton进行切换

    1.页面切换 <?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx=& ...

  3. 安装sphinx和coreseek

    sphinx简介 Sphinx是由俄罗斯人Andrew Aksyonoff开发的一个全文检索引擎.意图为其他应用提供高速.低空间占用.高结果 相关度的全文搜索功能.Sphinx可以非常容易的与SQL数 ...

  4. 【UOJ207】共价大爷游长沙(Link-Cut Tree,随机化)

    [UOJ207]共价大爷游长沙(Link-Cut Tree,随机化) 题面 UOJ 题解 这题太神了 \(\%\%\%myy\) 看到动态的维护边很容易的想到了\(LCT\) 然后能否堵住一条路 我们 ...

  5. 【BZOJ1996】合唱队(动态规划)

    [BZOJ1996]合唱队(动态规划) 题面 BZOJ 题解 很容易的一道题 因为每个人不是放在了左边就是放在了右边 所以每次放好的人必定是原序列的一个子串 所以,很容易想到区间\(dp\) 设\(f ...

  6. 【BZOJ4530】大融合(Link-Cut Tree)

    [BZOJ4530]大融合(Link-Cut Tree) 题面 讨厌权限题!!! Loj链接 题目描述 小强要在 N个孤立的星球上建立起一套通信系统.这套通信系统就是连接 N个点的一个树.这个树的边是 ...

  7. 【HNOI 2002 】营业额统计(splay)

    题面 Description Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营 ...

  8. [Luogu2073]送花

    题面 题目背景 小明准备给小红送一束花,以表达他对小红的爱意.他在花店看中了一些花,准备用它们包成花束. 题目描述 这些花都很漂亮,每朵花有一个美丽值W,价格为C. 小明一开始有一个空的花束,他不断地 ...

  9. eclipse中Maven项目pom.xml报错:com.thoughtworks.xstream.io.HierarchicalStreamDriver

    eclipse中创建Maven项目时 pom.xml报错:com.thoughtworks.xstream.io.HierarchicalStreamDriver 解决方案1.在pom文件中加入mav ...

  10. 简单谈谈js中Promise的用法

    首先先推荐一篇博文:http://blog.csdn.net/jasonzds/article/details/53717501 这篇博文很清晰的说明了Promise的用法,这里来简单总结一下: Pr ...