加盟披萨店
之前基于简单工厂的披萨店系统运行良好,那么继续进行拓展。
由于披萨店经营有成,以至于大家都希望自家附近有加盟店出现,身为加盟公司的经营者,也就是PizzaStore对象,自然希望确保加盟店运行的质量,所以希望这些店都使用PizzaStore中经过时间考验的代码。
但是区域的差异不能视而不见,由于加盟店的地理位置不同(比方说纽约店、芝加哥店、加州店),所使用的披萨也不同。
我们已经有一个做法
利用SimplePizzaFactory,修改它,写出三种不同的工厂,分别是NYPizzaFactory、ChicagoPizzaFactory、CaliforniaPizzaFactory,那么各地加盟店都有合适的工厂可以使用,这是一种做法。
但是你想要多一些的质量控制
在推广SimpleFactory时,你发现加盟店的确是采用你的工厂创建比萨,但是其他部分,却开始采用他们自创的流程。烘烤的做法有些差异、不要切片、使用其他厂商的盒子等等。
换句话说,加盟店只使用了工厂创建Pizza对象,然后不去调用Pizza对象的prepare()、bake()、cut()、box()方法。
给披萨店使用框架
有个做法可以让比萨制作活动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由的只做该地区的披萨。
所要做的事情就是把createPizza()方法放回到PizzaStore中,不过要把它设置称“抽象方法”,然后为每个区域风味创建一个PizzaStore的子类。
首先,先看看如何修改PizzaStore类:
/**
* 这里一个披萨店的抽象类。
* */
public abstract class PizzaStore {
/**
* 订购披萨的方法,返回一个披萨饼对象。
* */
public Pizza orderPizza(String type) {
// 通过披萨工厂创建披萨。
Pizza pizza = createPizza(type);
// 擀面上料、烘烤、切片、装盒。
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* 根据披萨类型创建指定的披萨对象。
* */
abstract Pizza createPizza(String type);
}
现在已经有一个PizzaStore作为超类,让每个地区的加盟店都创建一个PizzaStore子类(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore),每个子类各自决定如何制造披萨。让我们看看这要如何进行。
允许子类做决定
各个区域比萨店之间的差异在于它们制作比萨时的习惯(纽约比萨的饼薄,芝加哥比萨的饼厚等),我们现在让createPizza()能够应对这些变化。
做法如上图所示,让PizzaStore的子类负责定义自己的createPizza()方法,所以我们会得到一些PizzaStore具体的子类,每个子类都有自己的比萨实体生成方式,而仍然适合PizzaStore框架,饼使用已经写好的orderPizza方法()。下面看用代码如何实现:
public class NYStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza;
// 通过类型返回指定的披萨,但这些披萨都是纽约风味的。
if ("cheese".equals(type)) {
pizza = new NYStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new NYStylePepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new NYStyleClamPizza();
} else if ("veggie".equals(type)) {
pizza = new NYStyleVeggiePizza();
} else {
return null;
}
return pizza;
}
}
public class ChicagoStylePizzaFactory extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza;
// 通过类型返回指定的披萨,但这些披萨都是芝加哥风味的。
if ("cheese".equals(type)) {
pizza = new ChicagoStyleCheesePizza();
} else if ("pepperoni".equals(type)) {
pizza = new ChicagoStylePepperoniPizza();
} else if ("clam".equals(type)) {
pizza = new ChicagoStyleClamPizza();
} else if ("veggie".equals(type)) {
pizza = new ChicagoStyleVeggiePizza();
} else {
return null;
}
return pizza;
}
}
声明一个工厂方法
原本是一个对象负责所有具体类的实例化,现在通过对PizzaStore做一些小转变,变成由一群子类来负责实例化。让我们看的更仔细些:
PizzaStore nyStylePizzaStore = new NYStylePizzaStore();
1.建立一个NYStylePizzaStore的实例;
Pizza nyPizza = nyStylePizzaStore.orderPizza("veggie");
2.调用NYStylePizzaStore实例的orderPizza()方法,传递参数veggie,是告诉纽约披萨店我需要一个素食披萨(当然是纽约风格的素食比萨)。
Pizza pizza = createPizza(type);
3.orderPizza()方法去调用工厂方法——createPizza(),并将type传递给它。他返回一个纽约风格的素质比萨。
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
4.披萨店对比萨进行处理,返回给客户。不管那个地区的披萨店,都要进行这些处理,没有特别的,这样,之前所说的不进行切片,使用其他披萨店的盒子的问题就解决了!
认识工厂方法的时刻终于到来了
所有工厂模式都用来封装对象的创建,工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
工厂方法模式能够封装具体类型的实例化。看看下面的类图:
对于简单工厂与工厂方法的差异
他们看起来很类似,差别在于,在工厂方法中,返回比萨的类是子类(实现了PizzaStore的类)。具体解释一下,子类的确看起来像是一个简单工厂。
差别在于简单工厂会把全部的事情都做好,在一个地方都处理完了。然而工厂方法却是创建一个框架,让子类决定要如何实现。比方说,在工厂方法中,orderPizza()方法提供了一般的框架,以便创建比萨,orderPizza()方法一来工厂方法创建的具体类,饼制造出实际的比萨。可通过继承PizzaStore类,决定实际制造出的披萨是什么。
而简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。
依赖倒置原则
有一个OO设计原则就正式阐明了这一点:这个原则甚至还有一个又响亮又正式的名称:“依赖倒置原则”(Dependency Inversion Principle)。
3通则如下:
设计原则
要依赖抽象,不要依赖具体类。
下面的方针,能帮你避免在OO设计中违反依赖倒置原则:
- 变量不可以持有具体类的引用;
- 不要让类派生自具体类;
- 不要覆盖基类中已实现的方法;
但是,完全遵守这些似乎也不太可能,正如同许多原则一样,应该尽量去达到这个原则,而不是随时都要遵循这个原则。
以上便是工厂方法的一些知识。