定义
工厂方法模式(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原则
-
从上面Pizza的例子可以看出,创建具体对象可能是经常变化的部分,隔离出来,放到别的类就是简单工厂,放到子类去实现就是工厂方法或者抽象工厂模式
-
这个就多了,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(); } }
-
这个具体可以看另外一篇文章《面向对象的设计原则》关于原则六的说明部分
本章总结
所有的工厂都是用来封装对象创建的
简单工厂虽然不是真正的设计模式,但是是一个简单而且常用的方法,可以将客户程序从具体类解耦
工厂方法使用继承,把对象的创建延时到子类
抽象工厂使用对象组合,对象的创建被实现在工厂接口的方法中