四、工厂模式(Factory Pattern)《HeadFirst设计模式》读书笔记
工厂模式可以细分为三种类型:1)简单工厂 2)工厂方法 3)抽象工厂。简单工厂其实并不是一个设计模式,反而比较像是一种编程习惯。总体来说,工厂模式可以减少应用程序和具体类之间的依赖,促进松耦合,同时也方便后期的扩展。
1.简单工厂
很多时候我们都采用new的方式直接创建一个对象,但当创建对象的代码逻辑比较复杂,比如说要经过一系列的if-else语句返回具体符合条件的对象,一般我们都抽取出一个方法,使整体的代码结构更清晰,可读性更高。我们可以更近一步的将这个方法封装到另外一个类中,这个类就是简单工厂。
比如说在下面的PizzaStore类中,我们想要在orderPizza方法中返回对应类型的的Pizza:
public class PizzaStore { public Pizza orderPizza(String type){ Pizza pizza = null; if ("cheese".equals(type)) { pizza = new CheesePizza(); } else if ("greek".equals(type)) { pizza = new GreekPizza(); } else if ("pepperoni".equals(type)) { pizza = new PepperoniPizza(); } return pizza; } }
这样Pizza对象的创建就和PizzaStore紧紧耦合在了一起,当我们想扩展一些Pizza类型时只能去修改PizzaStore中的代码。因此我们可以将创建Pizza对象的的代码移到另外一个类中,由它专职来创建Pizza,这就是简单工厂:
public class SimplePizzaFactory { public Pizza createPizza(String type){ Pizza pizza = null; if ("cheese".equals(type)) { pizza = new CheesePizza(); } else if ("greek".equals(type)) { pizza = new GreekPizza(); } else if ("pepperoni".equals(type)) { pizza = new PepperoniPizza(); } return pizza; }
通过在PizzaStore的构造方法中传入SimplePizzaFactory,在orderPizza方法中调用它的createPizza方法:
public class PizzaStore { SimplePizzaFactory simplePizzaFactory; public PizzaStore(SimplePizzaFactory simplePizzaFactory) { this.simplePizzaFactory = simplePizzaFactory; } public Pizza orderPizza(String type){ return simplePizzaFactory.createPizza(type); } }
这样当我们需要扩展功能的时候,只需要修改SimplePizzaFactory类,而不需要修改客户代码,将对象的创建过程和具体的业务分离开来了。
优化:如果想要去掉if-else,可以把对象放到一个Map中,每次根据Key值去取。
2.工厂方法
工厂方法则是首先有一个抽象父类(接口/抽象类在本文中不做区分),抽象父类中有创建对象的抽象方法,由具体的子类去实现创建对象的细节。
如果不同的地方要开不同的PizzaStore分店,比如NewYorkPizzaStore,ChicagoPizzaStore,同时不同地方的PizzaStore生产的Pizza也有差异,又应该怎么设计呢。
从上面的要求中,我们可以抽象出两个抽象类,一是PizzaStore,它是所有类型PizzaStore的抽象父类;二是Pizza,它是所有类型Pizza的抽象父类。他们的关系如下:
上图中有两种类型的类,一种是我们需要的产品,他们的抽象父类是Pizza;另一个是创建者/工厂,他们的抽象父类是PizzaStore。在PizzaStore中我们只声明一个抽象的创建Pizza对象的方法,而在子类中具体实现这个方法返回具体的Pizza实例。简单的代码示例如下:
创建者类:
public abstract class PizzaStore { public abstract Pizza createPizza(); } public class NewYorkPizaStore extends PizzaStore { @Override public Pizza createPizza() { return new NewYorkPizza(); } } public class ChicagoPizaStore extends PizzaStore { @Override public Pizza createPizza() { return new ChicagoPizza(); } }
产品类:
public abstract class Pizza { String type; public String getType() { return type; } } public class NewYorkPizza extends Pizza { public NewYorkPizza() { type = "NewYorkPizza"; } } public class ChicagoPizza extends Pizza { public ChicagoPizza() { type = "ChicagoPizza"; } }
测试:
public class Test01 { public static void main(String[] args) { PizzaStore pizzaStore = new NewYorkPizaStore(); Pizza pizza = PizzaStore.createPizza(); System.out.println(pizza.getType()); } }
结果会输出NewYorkPizza,以上这个例子简单的说明了工厂方法的使用,下面看下它的具体定义:
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
在上面例子中的PizzaStore就是一个创建对象的接口,它提供了抽象方法createPizza()来创建产品Pizza,这个createPizza()方法就是一个工厂方法,它返回Pizza接口,但是返回什么类型的Pizza是在子类中实现的。上面Test01中是直接new了一个PizzaStore的实现类NewYorkPizasStore,所以看起来下面调用createPizza()方法时已经知道了是NewYorkPizasStore调用的了,但实际情况可能是通过多态,在构造方法得到的子类对象,这样再调用createPizza()方法的时候就不知道运行时会传进来的具体实现类,也就不会知道最后返回的Pizza会是哪一种了。
那么简单工厂和工厂方法的区别是什么呢?
1)简单工厂是通过将具体对象创建封装到一个简单工厂类中来实现解耦的;而工厂方法是将具体对象创建的过程放到子类中进行,子类只需要重写父类的抽象创建方法即可。
2)简单工厂弹性差,每次新增一个产品实例都需要修改简单工厂中的代码,不能动态扩展;而工厂方法在扩展时只需要新增子类,可以变更正在创建的产品。
在JDK中,Collection接口有一个获得迭代器的抽象方法iterator(),返回的是Iterator接口,在它的间接的实现类ArrayList中iterator()方法就被重写返回了一个Itr迭代器,而Itr就实现了Iterator接口。这其实
就采用了工厂方法模式。
3.抽象工厂
定义:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
由以上的定义可以看出抽象工厂模式用来创建一个“家族”,在上面工厂方法的例子中我们只需要创建一个产品也就是Pizza,所以也只有一个createPizza()工厂方法,但是如果我们创建的是一个产品家族,比如说披萨的各种配料,那就每种配料都需要一个create方法来创建,抽象工厂实际上就是要创建多个产品,而每种产品都使用工厂方法这种模式,在产品父类中定义抽象的create方法,在子类中具体实现。
抽象工厂模式就不代码演示了,直接借用书中的一个类图来说明就很清晰明了:
图中的创建者类:PizzaIngredientFactory和它的两个子类,用来表示原料工厂。
图中的产品类:Dough、Sauce、Cheese、Clams和各自的子类,用来表示原料产品。
如果调用者组合了上述四种原料产品,就可以用抽象工厂模式,用具体的子类工厂去创建对应的每一种原料。
4.总结
1)简单工厂就是将对象的创建放到一个专门的工厂类中,严格上来说不算是设计模式,而是一种编程习惯;
2)工厂方法是将创建对象的具体方法放在子类中,更方便动态的扩展,理清产品的父子类和创建者的父子类关系有助于理解工厂方法;
3)抽象工厂就是涉及一族产品组合在一起时使用,也就是运用了多个工厂方法来创建一族对象;
这一章书中的内容很多,看起来有点乱,后来也是通过查询其他资料再反复读一遍书才大致明白了。本文中的例子并没有完全按照书中来,因为书中是一个例子循序渐进贯穿了简单工厂、工厂方法和抽象工厂,最后工厂方法和抽象工厂混合在了一起,因此看起来会有点乱。看到这一章发现即使看懂了这些设计方法但却还是不太知道具体项目中要如何使用,主要还是没有实践经验,后续可能会结合看源码加深对设计模式的理解,并争取能多在项目中合理的应用到一些设计模式。