工厂模式
其实,工厂模式并不是一个标准的设计模式,只是使用的多了(尤其在spring框架中) 也就演变成一个模式了。这里记录一下这个特殊模式的使用。我们依然会用到书本上的例子来展示这个模式。
OO原则
- 对修改“关闭” 对扩展开放
pizza工厂
假设我们这里是一个披萨店,现在需要给客户提供pizza。为了简便,我们就简单地“制作”pizza。我们工序分为准备prepare,烘焙bake,切片cut,装盒box。这样就可以配送或者直接上桌了。这里我们简单地定义一个pizza类
public abstract class Pizza {
String name;
String dough;
String sauce;
void prepare() {
System.out.println("Preparing " + name);
System.out.println("Tossing dough...");
System.out.println("Adding sauce");
System.out.println("Adding toppings: ");
}
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official pizzaStore box");
}
public String getName () {
return name;
}
}
这是一个抽象类,下面我们就要“成产”他的实现类了,例如CheesePizza, PepperoniPizza.....等。这些实现类就是店里不同type的pizza实体类,每次客户下了订单,就实例化一个特定的pizza类。那么,我们的代码是什么样的呢?很明显这里会有一个type参数,根据不同的type我们去实例化不同的pizza。例如
Pizza orderPizza(String type) {
Pizza pizza;
if (type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
}
/**
* for sometimes, we undercarriage some type of
* pizzas or create some new type of pizzas so
* we use comment to represent that some type of
* pizzas are undercharged
*/
// else if (type.equals("greek")) {
// pizza = new GreekPizza();
// } else if (type.equals("pepperoni")) {
// pizza = new PepperoniPizza();
// }
else {
pizza = new NYStyleCheesePizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
上面的注释也说明了一些问题,当我们一直运营我们的pizza店,有些类型的pizza下架又或是推出新的pizza那我们就要不断地去修改代码(不断地去修改if-else语句),那显然是不够灵活的并且破坏了我们"对修改关闭,对扩展开放"的原则。这个时候就需要我们的工厂模式出场了。我们完全可以把if-else的判断语句抽象出去,order方法里根本就不需要关心这些实例化的pizza类。其实很多人第一反应也是这样,但是我们这里稍微调整一下。我们抽象出一个创建实例的抽象方法,并且让子类去实现到底要创建什么样的类,这样做有什么好处呢?举个例子,每个国家都有麦当劳,但是不同的国家会根据当地的习俗创建或修改麦当劳里的食物,比如澳洲的麦当劳有一种汉堡叫做quarter pounder,但是国内就没有这种汉堡。现在我们做pizza,但是不是每一个地方的cheese味pizza都一毛一样,所以我们把创建实例的方法抽象出来,让子类去实现它需要的具体的cheese味pizza,到底重口味版的还是清淡版的,完全取决于商家自己。那么代码就应该是这样的:
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
protected abstract Pizza createPizza(String type); // 这里就是抽象方法 让子类自己去实现
}
如果纽约要开一家Pizza店,那只需要继承这个抽象类,然后实现这个抽象方法就行。具体要制作出什么样的pizza就完全由这个商家自己决定
public class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new NYStyleCheesePizza();
}
return null;
}
}
看到这里是不是对工厂方法有了不一样的看法?下面,认识工厂方法模式的时刻终于到了。
工厂方法模式就是通过子类决定实例化哪一个类,来达到将对象创建的过程封装的目的。工厂方法让类把实例化推迟到了子类。
抽象工厂模式
刚刚上面的例子,我们只是做了简单的封装而已,但是在实际开发中并不会这么简单。因为很多时候一个类多多少少都会去依赖另外一个类(或者使用到别的类) 我们还是用pizza作为例子,虽然pizza这个抽象类只是定义几个属性字段,但是不同的地方对于dough或者sauce都是不同的,那我们怎么去应对呢?(这里偷了一个懒,正常原料也应该是一个对象,但是这里都使用了string类型来区分不同的原料)这里就要请出抽象工厂了,就是在创造pizza的同时也有一个"原料“工厂来为他们生成特定的原料配方。
public interface PizzaIngredientFactory {
public String createDough();
public String createSauce();
}
然后根据不同的地区或者区域,去实现这个工厂,例如
public class NYPizzaIngrediantFactory implements PizzaIngredientFactory {
@Override
public String createDough() {
return "NY style Dough";
}
@Override
public String createSauce() {
return "NY style sauce";
}
}
最后我们在修改一下,pizza实现类。
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza (PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
public void prepare() {
System.out.println("Preparing " + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
}
}
这样,每次我们的原料就可以从工厂中去取,完全不用知道我要取的是什么原料,只要给我正确的原料就行。同时,对于原来内部,如果有别的、多个依赖,也可以继续套用抽象工厂来为其生产原料的原料.....以此下去,这就是抽象工厂模式。
抽象工厂模式就是提供一个借口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
这里的抽象工厂模式有点之前写的策略模式的味道,只是一个是专注于变化的行为,一个是专注于变化的实体对象。
总结
- 工厂方法使用继承:把对象的创建委托给子类,子类实现工厂方法来创建对象。
- 抽象工厂使用对象组合:对象的创建被实现在工厂借口所暴露的方法中。
- 工厂是很有威力的技巧,帮助我们针对抽象编程,而不要针对具体类编程。
- 所有工厂模式都是通过减少应用程序和具体类之间的依赖促进松耦合。