浅析设计模式(二)——工厂方法模式
工厂方法模式(Factory-Method,创建型模式)
本文的结构:
一、工厂方法模式的定义
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method使一个类的实例化延迟到子类。
从工厂方法的实现来看,它通过继承来实现,委托子类把创建对象的方法具体化,通常用于创建单个产品。
上一篇【浅析设计模式(四)——创建型模式之Simple-Factory(简单工厂方法,非设计模式)】中介绍的简单工厂方法,虽然已经对变化的部分进行了封装,但是这里只由一个对象负责所有的具体类的实例化,因此每次有新增对象类型时,都需要改变工厂的源码进行扩展。
如果把实例化部分(即上面创建对象的createPizza方法)进行抽象,而且挪到PizzaStore(改为抽象类,即由抽象类定义上层接口)里面,然后由子类负责具体实现,那就可以使用一群子类来负责实例化了,而不用改变原有的子类实现及其实例化方式,比较容易进行扩展,当然,缺点就是需要维护一群子类,每次有新的类型时,在不修改原有实现的基础上,只能通过新增子类来进行实现;另外一点就是需要在运行时根据实际情况进行选择和确认具体类。简单工厂方法可以看成是工厂方法模式退化后的一种特例,即在确定需要实例化的对象基本不会变化、而且可控的情况下,将抽象的工厂方法与具体的工厂方法进行合并,始终由单一的具体工厂直接负责对象的实例化。
二、工厂方法模式的参与者及其角色
1. Product
- 定义工厂方法要创建的对象的接口
2. ConcreteProduct
- 实现Product接口,即具体的对象
3. Creator
- 声明工厂方法,该方法返回一个Product类型的对象。Creator也可以定义一个工厂方法的缺省实现,他返回一个缺省的ConcreteProduct对象。
4. ConcreteCreator
- 重定义工厂方法以返回一个ConcreteProduct对象。
三、工厂方法模式的类图
四、工厂方法模式的示例
1. Creator:这个是工厂方法的上层接口,定义了创建对象的接口方法,也即对创建对象的方法进行了抽象化。
这里还是从创建Pizza开涮。可以看出与简单工厂方法的差别,这里又把创建对象的方法搬回来了,只是抽象化了,没有具体实现,目的是要让子类类确认实例化的具体实现。其他的流程还是可以保持一致的。
1 /** 2 * This is the factory. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public abstract class PizzaStore { 8 public abstract Pizza createPizza(String item); 9 10 public Pizza orderPizza(String type) { 11 Pizza pizza = createPizza(type); 12 System.out.println("--- Making a " + pizza.getName() + " ---"); 13 pizza.prepare(); 14 pizza.bake(); 15 pizza.cut(); 16 pizza.box(); 17 return pizza; 18 } 19 }
2. ConcreteCreator:Creator的实现类,在这些实现子类中进行了具体的实现,也即实际创建对象的动作在这里发生。
实现类1:纽约风味的PizzaStore
1 /** 2 * This is the concrete factory-method. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public class NYPizzaStore extends PizzaStore { 8 9 // Here is the concrete creator. 10 @Override 11 public Pizza createPizza(String item) { 12 if (item.equals("cheese")) { 13 return new NYStyleCheesePizza(); 14 } else if (item.equals("veggie")) { 15 return new NYStyleVeggiePizza(); 16 } else if (item.equals("clam")) { 17 return new NYStyleClamPizza(); 18 } else if (item.equals("pepperoni")) { 19 return new NYStylePepperoniPizza(); 20 } else 21 return null; 22 } 23 24 }
实现类2:芝加哥风味的PizzaStore
1 /** 2 * This is the concrete factory-method. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public class ChicagoPizzaStore extends PizzaStore { 8 9 // Here is the concrete creator. 10 @Override 11 public Pizza createPizza(String item) { 12 if (item.equals("cheese")) { 13 return new ChicagoStyleCheesePizza(); 14 } else if (item.equals("veggie")) { 15 return new ChicagoStyleVeggiePizza(); 16 } else if (item.equals("clam")) { 17 return new ChicagoStyleClamPizza(); 18 } else if (item.equals("pepperoni")) { 19 return new ChicagoStylePepperoniPizza(); 20 } else 21 return null; 22 } 23 24 }
当然还可以添加南加州风味、洛杉矶风味等等的PizzaStore。
3. Product:需要创建的对象的父类。
实际上,要创建的是一类对象,并不是单纯的某一个对象,因此,这里的抽象化也是很有必要的,而且后续的维护、扩展等也是基于抽象类来开展,进行实际的定制化、具体化。
1 import java.util.ArrayList; 2 3 /** 4 * This is the abstract product. 5 * <p> 6 * Reference : Head-First-Design-Patterns 7 * 8 */ 9 public abstract class Pizza { 10 String name; 11 String dough; 12 String sauce; 13 ArrayList<String> toppings = new ArrayList<String>(); 14 15 void prepare() { 16 System.out.println("Prepare " + name); 17 System.out.println("Tossing dough..."); 18 System.out.println("Adding sauce..."); 19 System.out.println("Adding toppings: "); 20 for (String topping : toppings) { 21 System.out.println(" " + topping); 22 } 23 } 24 25 void bake() { 26 System.out.println("Bake for 25 minutes at 350"); 27 } 28 29 void cut() { 30 System.out.println("Cut the pizza into diagonal slices"); 31 } 32 33 void box() { 34 System.out.println("Place pizza in official PizzaStore box"); 35 } 36 37 public String getName() { 38 return name; 39 } 40 41 public String toString() { 42 StringBuffer display = new StringBuffer(); 43 display.append("---- " + name + " ----\n"); 44 display.append(dough + "\n"); 45 display.append(sauce + "\n"); 46 for (String topping : toppings) { 47 display.append(topping + "\n"); 48 } 49 return display.toString(); 50 } 51 }
4. ConcreteProduct:需要创建的具体对象,后续需要扩展新的对象,实现Pizza类即可。
示例1:
1 /** 2 * This is the concrete product. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public class NYStyleCheesePizza extends Pizza{ 8 public NYStyleCheesePizza() { 9 name = "NY Style Sauce and Cheese Pizza"; 10 dough = "Thin Crust Dough"; 11 sauce = "Marinara Sauce"; 12 13 toppings.add("Grated Reggiano Cheese"); 14 } 15 }
示例2:
1 /** 2 * This is the concrete product. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public class ChicagoStyleCheesePizza extends Pizza{ 8 public ChicagoStyleCheesePizza() { 9 name = "Chicago Style Deep Dish Cheese Pizza"; 10 dough = "Extra Thick Crust Dough"; 11 sauce = "Plum Tomato Sauce"; 12 13 toppings.add("Shredded Mozzarella Cheese"); 14 } 15 16 void cut() { 17 System.out.println("Cutting the pizza into square slices"); 18 } 19 }
其他的就省略了,具体可看后面的参考。。。。
5. 测试
主要是使用上面的具体PizzaStore来创建各类Pizza。
1 /** 2 * This is the test-main. 3 * <p> 4 * Reference : Head-First-Design-Patterns 5 * 6 */ 7 public class PizzaTestApp { 8 public static void main(String[] args) { 9 10 //2 concrete factory 11 PizzaStore nyStore = new NYPizzaStore(); 12 PizzaStore chicagoStore = new ChicagoPizzaStore(); 13 14 Pizza pizza = nyStore.orderPizza("cheese"); 15 System.out.println("Ethan ordered a " + pizza.getName() + "\n"); 16 17 pizza = chicagoStore.orderPizza("cheese"); 18 System.out.println("Joel ordered a " + pizza.getName() + "\n"); 19 20 pizza = nyStore.orderPizza("clam"); 21 System.out.println("Ethan ordered a " + pizza.getName() + "\n"); 22 23 pizza = chicagoStore.orderPizza("clam"); 24 System.out.println("Joel ordered a " + pizza.getName() + "\n"); 25 26 pizza = nyStore.orderPizza("pepperoni"); 27 System.out.println("Ethan ordered a " + pizza.getName() + "\n"); 28 29 pizza = chicagoStore.orderPizza("pepperoni"); 30 System.out.println("Joel ordered a " + pizza.getName() + "\n"); 31 32 pizza = nyStore.orderPizza("veggie"); 33 System.out.println("Ethan ordered a " + pizza.getName() + "\n"); 34 35 pizza = chicagoStore.orderPizza("veggie"); 36 System.out.println("Joel ordered a " + pizza.getName() + "\n"); 37 } 38 }
五、参考
1、参考《Head First设计模式》和GoF《设计模式:可复用面向对象软件的基础》