前言
一家披萨店PizzaStore有多种类型的披萨,有cheese、clam、veggle等等。当顾客订购披萨时,可通过如下方式实现:
public class PizzaStore {
private Pizza pizza;
public Pizza orderPizza(String type){
if (type.equals("cheese")){
pizza = new CheesePizza();
} else if (type.equals("clam")){
pizza = new ClamPizza();
}
...
//pizza处理
pizza.prepare();
pizza.bake();
pizza.cut();
...
return Pizza;
}
}
在这里,是可以将生成哪种的Pizza从PizzaStore中移植出来,专门用一个类生成具体Pizza。
简单工厂
我们用一个PizzaFactory专门生成Pizza。
public class PizzaFactoty {
public Pizza createPizza(String type){
if (type.equals("cheese")){
pizza = new CheesePizza();
} else if (type.equals("clam")){
pizza = new ClamPizza();
}
...
return pizza;
}
}
然后PizzaStore注入PizzaFactory对象。用来生成Pizza对象。
public class PizzaStore {
private Pizza pizza;
private PizzaFactoty factoty;
public PizzaStore(PizzaFactoty factoty){
this.factoty = factoty;
}
public Pizza orderPizza(String type){
pizza = factoty.createPizza(type) ;
....
return Pizza;
}
}
将Pizza的生产放在一个专门的工厂类中,这就是所谓的简单工厂。其实简单工厂并不算是一个设计模式,只是一种编程习惯。
需求升级
这家披萨店在不同区域都开了分店,披萨的制作流程一样,但是由于不同区域开的店对于同品种的披萨口味要求不一样。比如同一样cheese披萨,在纽约和芝加哥的口味是不一样的。这时候该如何实现这种需求呢?
工厂方法
这时候工厂方法就派上用场了。我们中PizzaStore用一个抽象类用来生成Pizza,让具体的不同地区PizzaStore分店实现具体的Pizza生成。
public abstract class PizzaStore {
private Pizza pizza;
public Pizza orderPizza(String type){
pizza = createPizza();
pizza.prepare();
pizza.bake();
...
return Pizza;
}
public abstract Pizza createPizza(String type);
}
对于纽约的披萨分店,继承PizzaStore并实现createPizza()方法。
public class NewYorkPizzaStore extends PizzaStore{
private Pizza pizza;
@Override
public Pizza createPizza(String type) {
if (type.equals("cheese")){
pizza = new NewYorkCheesePizza();
} else if (type.equals("clam")){
pizza = new NewYorkClamPizza();
}
return pizza;
}
}
而对于芝加哥的披萨分店:
public class ChicagoPizzaStore extends PizzaStore{
private Pizza pizza;
@Override
public Pizza createPizza(String type) {
if (type.equals("cheese")){
pizza = new ChicagoCheesePizza();
} else if (type.equals("clam")){
pizza = new ChicagoClamPizza();
}
return pizza;
}
}
于是就实现不同区域分店同一品种披萨不同口味的需求。这就是所谓的工厂方法。它将具体的对象的生成放在子类中进行。父类只需面对抽象接口编程,也就是所谓的要依赖抽象,不要依赖具体类这一设计原则。
需求再升级
不同区域同一品种的披萨口味不一样其实是因为所用原材料是不一样的。为了防止某个区域的分店因为原材料不一样私自偷工减料,影响品牌口碑。于是需要有一个原材料工厂给各个区域供原材料。
抽象工厂
我们建一个原材料工厂,包括所有品种的原材料。
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
....
}
对于纽约分店所需要的材料:
public class NewYorkPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough(){
}
public Sauce createSauce(){
}
public Cheese createCheese(){
}
....
}
对于芝加哥分店所需要的材料:
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough(){
}
public Sauce createSauce(){
}
public Cheese createCheese(){
}
....
}
前面工厂方法中所说的纽约口味NewYorkCheesePizza和芝加哥口味cheese披萨。其实只是原材料不同而已,都属于cheese披萨。没必要需要用两个类实现。
public class CheesePizza extends Pizza {
private PizzaIngredientFactory pizzaIngredientFactory
public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory){
this.pizzaIngredientFactory = pizzaIngredientFactory;
}
public void prepare(){
dough = pizzaIngredientFactory.createDough();
sauce = pizzaIngredientFactory.createSauce();
...
}
public void bake{
}
public void cut{
}
....
}
于是,对于纽约的披萨店来说,其订购披萨的实现如下:
public class NewYorkPizzaStore extends PizzaStore{
private Pizza pizza;
private PizzaIngredientFactory pizzaIngredientFactory = new NewYorkPizzaIngredientFactory();
@Override
public Pizza createPizza(String type) {
if (type.equals("cheese")){
pizza = new CheesePizza(pizzaIngredientFactory);
} else if (type.equals("clam")){
pizza = new ClamPizza(pizzaIngredientFactory);
}
return pizza;
}
}
其实,这里也继承了前面工厂方法的思想,具体的createPizza对象是通过子类生成的。
这样,我们通过抽象工厂的方法将原材料的供给从披萨中解耦出来。
工厂方法和抽象工厂的区别
- 抽象工厂和工厂方法都是负责创建对象,只是工厂方法使用的是继承的方式,而抽象工厂是通过组合的方式。如工厂方法中的 NewYorkPizzaStore 继承 PizzaStore ,抽象工厂中实现的 new CheesePizza(pizzaIngredientFactory)。
- 抽象工厂中当加入新产品的时候,需要改变接口,这是抽象工厂的缺点。当相对于工厂方法来说,工厂方法只能创建一个产品,如上面的PizzasStore,而抽象工厂可以创建整个产品家族,如 CheesePizza,ClamPizza。而且对于原材料来说,里面的Dough、Sauce等材料都可以是抽象类,针对不同产品可以拥有不同口味的Dough和Sauce。
- 当需要创建一个产品家族的时候,用抽象工厂。当只是把一个产品从需要实例化该产品的具体类中解耦出来,就使用工厂方法。
UML图
以上面的Pizza场景为例,可以用UML图展示工厂方法和抽象工厂是如何实现的和之间的区别。