《HeadFirst设计模式》读书笔记-第4章-工厂模式
2017-03-18 20:43
701 查看
定义
工厂方法模式(factory method pattern)定义了一个创建对象的接口,由子类决定要实例化的类是哪一个,让类的实例化推迟到子类。从类图可以看出,工厂方法模式封装了具体类型的实例化,抽象的Creator类提供了一个抽象方法用来创建产品,同时也实现了一些操作产品的方法(如anOperation),这些方法不需要涉及ConcreteProduct,而是通过Product接口。所以,工厂方法模式帮助我们把产品的创建(子类实现的factoryMethod方法)从使用中(Creator中anOperation等方法)解耦。工厂方法的签名如下,
abstract Product factoryMethod(String type)
几点说明:
1. 工厂方法一般是abstract的,但是也可以在定义时提供一个默认的工厂方法,这样可以在不实现子类的情况下,也能创建产品
2. 工厂方法返回的是抽象类型,不是具体的类型
3. 此处通过参数type创建不同的产品,然而工厂经常只产生一种对象,不需要参数化
抽象工厂模式(abstract factory pattern)提供了一个接口,用于创建相关或者依赖对象的家族,而不需要明确指定具体类。
从类图可以看出,抽象工厂允许客户(Client)使用抽象的接口(AbstractFactory)来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。这样一来,客户就从具体的产品创建中被解耦。
代码实现
首先来介绍简单工厂,它不是一个设计模式,但是经常被使用。设计思想就是把创建具体对象这种会变化的部分独立出来,放到独立的类中,就是简单工厂,类图如下。PizzaStore.java
public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; // 为了隔离变化,不直接创建,而是交给简单工厂去做 // 创建对象需要具体类型 pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
SimplePizzaFactory.java
public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; // 创建具体类型 if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("pepperoni")) { pizza = new PepperoniPizza(); } else if (type.equals("clam")) { pizza = new ClamPizza(); } else if (type.equals("veggie")) { pizza = new VeggiePizza(); } return pizza; } }
具体产品CheesePizza
public class CheesePizza extends Pizza { public CheesePizza() { name = "Cheese Pizza"; dough = "Regular Crust"; sauce = "Marinara Pizza Sauce"; toppings.add("Fresh Mozzarella"); toppings.add("Parmesan"); } }
具体产品PepperoniPizza
public class PepperoniPizza extends Pizza { public PepperoniPizza() { name = "Pepperoni Pizza"; dough = "Crust"; sauce = "Marinara sauce"; toppings.add("Sliced Pepperoni"); toppings.add("Sliced Onion"); toppings.add("Grated parmesan cheese"); } }
产品基类Pizza
import java.util.ArrayList; abstract public class Pizza { String name; String dough; String sauce; ArrayList toppings = new ArrayList(); public String getName() { return name; } public void prepare() { System.out.println("Preparing " + name); } public void bake() { System.out.println("Baking " + name); } public void cut() { System.out.println("Cutting " + name); } public void box() { System.out.println("Boxing " + name); } public String toString() { // code to display pizza name and ingredients StringBuffer display = new StringBuffer(); display.append("---- " + name + " ----\n"); display.append(dough + "\n"); display.append(sauce + "\n"); for (int i = 0; i < toppings.size(); i++) { display.append((String )toppings.get(i) + "\n"); } return display.toString(); } }
测试驱动代码,
public class PizzaTestDrive { public static void main(String[] args) { SimplePizzaFactory factory = new SimplePizzaFactory(); PizzaStore store = new PizzaStore(factory); Pizza pizza = store.orderPizza("cheese"); System.out.println("We ordered a " + pizza.getName() + "\n"); pizza = store.orderPizza("veggie"); System.out.println("We ordered a " + pizza.getName() + "\n"); } }
上面介绍的简单工厂,是把创建对象的操作放在了另外一个对象了,如果放在类的方法里,由子类来负责实例化,就是工厂方法模式。下面给出由工厂方法模式实现PizzaStore的类图。
PizzaStore.java
public abstract class PizzaStore { // 工厂方法是抽象的,返回创建的产品Pizza // 工厂方法将客户(orderPizza方法)和实际创建具体产品的代码解耦 abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.getName() + " ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
Pizza.java
public abstract class Pizza { String name; String dough; String sauce; ArrayList toppings = new ArrayList(); void prepare() { System.out.println("Preparing " + name); System.out.println("Tossing dough..."); System.out.println("Adding sauce..."); System.out.println("Adding toppings: "); for (int i = 0; i < toppings.size(); i++) { System.out.println(" " + toppings.get(i)); } } 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; } public String toString() { StringBuffer display = new StringBuffer(); display.append("---- " + name + " ----\n"); display.append(dough + "\n"); display.append(sauce + "\n"); for (int i = 0; i < toppings.size(); i++) { display.append((String )toppings.get(i) + "\n"); } return display.toString(); } }
ChicagoPizzaStore.java
public class ChicagoPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new ChicagoStyleCheesePizza(); } else if (item.equals("veggie")) { return new ChicagoStyleVeggiePizza(); } else if (item.equals("clam")) { return new ChicagoStyleClamPizza(); } else if (item.equals("pepperoni")) { return new ChicagoStylePepperoniPizza(); } else return null; } }
ChicagoStyleCheesePizza.java
public class ChicagoStyleCheesePizza extends Pizza { public ChicagoStyleCheesePizza() { name = "Chicago Style Deep Dish Cheese Pizza"; dough = "Extra Thick Crust Dough"; sauce = "Plum Tomato Sauce"; toppings.add("Shredded Mozzarella Cheese"); } void cut() { System.out.println("Cutting the pizza into square slices"); } }
ChicagoStyleClamPizza.java
public class ChicagoStyleClamPizza extends Pizza { public ChicagoStyleClamPizza() { name = "Chicago Style Clam Pizza"; dough = "Extra Thick Crust Dough"; sauce = "Plum Tomato Sauce"; toppings.add("Shredded Mozzarella Cheese"); toppings.add("Frozen Clams from Chesapeake Bay"); } void cut() { System.out.println("Cutting the pizza into square slices"); } }
NYPizzaStore.java
public class NYPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; } }
NYStyleCheesePizza.java
public class NYStyleCheesePizza extends Pizza { public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza"; dough = "Thin Crust Dough"; sauce = "Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); } }
NYStyleClamPizza.java
public class NYStyleClamPizza extends Pizza { public NYStyleClamPizza() { name = "NY Style Clam Pizza"; dough = "Thin Crust Dough"; sauce = "Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); toppings.add("Fresh Clams from Long Island Sound"); } }
测试启动代码
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); PizzaStore chicagoStore = new ChicagoPizzaStore(); // 客户只要调用orderPizza方法,就创建了对应区域的对应口味的Pizza Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("clam"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("clam"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("pepperoni"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("pepperoni"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); pizza = nyStore.orderPizza("veggie"); System.out.println("Ethan ordered a " + pizza.getName() + "\n"); pizza = chicagoStore.orderPizza("veggie"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); } }
下面给出使用抽象工厂模式实现采用不同原料创建不同Pizza的例子,类图如下。
首先给出抽象工厂接口定义:
public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); }
Chicago风味的Pizza原料工厂实现
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThickCrustDough(); } public Sauce createSauce() { return new PlumTomatoSauce(); } public Cheese createCheese() { return new MozzarellaCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new BlackOlives(), new Spinach(), new Eggplant() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FrozenClams(); } }
NewYork风味的Pizza原料工厂实现
public class NYPizzaIngredientFactory implements PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheese() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClam() { return new FreshClams(); } }
类图中的原料品种太多,这里给出两种的实现,但是不会影响问题的说明。
Dough接口定义
public interface Dough { public String toString(); }
两种Dough原料的实现
public class ThickCrustDough implements Dough { public String toString() { return "ThickCrust style extra thick crust dough"; } }
public class ThinCrustDough implements Dough { public String toString() { return "Thin Crust Dough"; } }
Sauce接口定义
public interface Sauce { public String toString(); }
两种Sauce原料的实现
public class MarinaraSauce implements Sauce { public String toString() { return "Marinara Sauce"; } }
public class PlumTomatoSauce implements Sauce { public String toString() { return "Tomato sauce with plum tomatoes"; } }
PizzaStore.java
public abstract class PizzaStore { protected abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.getName() + " ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
NYPizzaStore.java
public class NYPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("New York Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("New York Style Clam Pizza"); } else if (item.equals("pepperoni")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("New York Style Pepperoni Pizza"); } return pizza; } }
ChicagoPizzaStore.java
public class ChicagoPizzaStore extends PizzaStore { protected Pizza createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory(); if (item.equals("cheese")) { pizza = new CheesePizza(ingredientFactory); pizza.setName("Chicago Style Cheese Pizza"); } else if (item.equals("veggie")) { pizza = new VeggiePizza(ingredientFactory); pizza.setName("Chicago Style Veggie Pizza"); } else if (item.equals("clam")) { pizza = new ClamPizza(ingredientFactory); pizza.setName("Chicago Style Clam Pizza"); } else if (item.equals("pepperoni")) { pizza = new PepperoniPizza(ingredientFactory); pizza.setName("Chicago Style Pepperoni Pizza"); } return pizza; } }
Pizza.java
public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare(); 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"); } void setName(String name) { this.name = name; } String getName() { return name; } public String toString() { StringBuffer result = new StringBuffer(); result.append("---- " + name + " ----\n"); if (dough != null) { result.append(dough); result.append("\n"); } if (sauce != null) { result.append(sauce); result.append("\n"); } if (cheese != null) { result.append(cheese); result.append("\n"); } if (veggies != null) { for (int i = 0; i < veggies.length; i++) { result.append(veggies[i]); if (i < veggies.length-1) { result.append(", "); } } result.append("\n"); } if (clam != null) { result.append(clam); result.append("\n"); } if (pepperoni != null) { result.append(pepperoni); result.append("\n"); } return result.toString(); } }
CheesePizza.java
public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } // 使用抽象工厂创建原料 void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
ClamPizza.java
public class ClamPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public ClamPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); clam = ingredientFactory.createClam(); } }
从上面可以很容易地看出,抽象工厂创建的是一个产品家族,而工厂方法创建的是一个具体产品,这是它们的区别。抽象工厂定义的接口中,每一个方法负责创建一个特定的产品,这个方法一般都是采用工厂方法来的实现的。
包含的OO原则
原则1从上面Pizza的例子可以看出,创建具体对象可能是经常变化的部分,隔离出来,放到别的类就是简单工厂,放到子类去实现就是工厂方法或者抽象工厂模式
原则2
这个就多了,orderPizza方法里,调用Pizza的prepare, bake,cut,box等方法,这些方法都是接口定义的,不依赖于具体类型。
public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.getName() + " ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }
还有抽象方法模式里,具体Pizza对象的prepare方法实现,调用接口创建各种原料。
public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
原则6
这个具体可以看另外一篇文章《面向对象的设计原则》关于原则六的说明部分
本章金句
所有的工厂都是用来封装对象创建的简单工厂虽然不是真正的设计模式,但是是一个简单而且常用的方法,可以将客户程序从具体类解耦
工厂方法使用继承,把对象的创建延时到子类
抽象工厂使用对象组合,对象的创建被实现在工厂接口的方法中
相关文章推荐
- 《软件设计精要与模式》读书笔记(五)&工厂模式资料汇总&提议
- Delphi 设计模式:《HeadFirst设计模式》Delphi代码---工厂模式之抽象工厂
- 《Head First设计模式》 读书笔记05 工厂模式(二)
- ios设计模式读书笔记----Factory Method(工厂方法)
- 读书笔记系列1:大话设计模式 -- 简单工厂模式
- 创建型模式--各种工厂模式[读书笔记]
- 《HeadFirst设计模式》学习笔记3-工厂模式
- 《Head First设计模式》 读书笔记05 工厂模式(二)
- (第Ⅱ部分 创建型模式篇) 第4章 工厂方法(Factory Method)
- 【读书笔记】读《JavaScript设计模式》之工厂模式
- Delphi 设计模式:《HeadFirst设计模式》Delphi2007代码---工厂模式之工厂方法
- 设计模式读书笔记之工厂方法模式
- (第Ⅱ部分 创建型模式篇) 第4章 工厂方法(Factory Method)
- 《大话设计模式》读书笔记(C++代码实现) 第一章:简单工厂模式
- 【读书笔记-重构与模式】 抽象工厂与单例模式的组合使用
- 《大话设计模式》读书笔记一 简单工厂模式
- 《大话设计模式》读书笔记-第1章 简单工厂模式
- 【读书笔记】HeadFirst设计模式——命令模式简述
- 设计模式读书笔记-----工厂方法模式
- 读书笔记_java设计模式深入研究 第三章 工厂模式 Factory