参考:Head First设计模式
概述
简单工厂模式实现了生成产品类的代码与客户端代码分离,在工厂类中可以添加生成产品的逻辑代码。
但是简单工厂模式不符合“开放-封闭”原则。例如要加一个 新产品类,就要修改 工厂类 生成产品的逻辑代码,增加
if-else
判断。对于这个问题,工厂方法模式可以解决。
定义
工厂方法模式 定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法模式 让类把实例化推迟到子类。
类图
- 角色
- 抽象产品类 : Product
- 具体产品类 : ConcreteProductA 和 ConcreteProductB
- 抽象工厂类 : AbstractFactory
- 具体工厂类 : ConcreteFactoryA 和 ConcreteFactoryB
实例
还是以披萨店的披萨订单为例,用工厂方法模式来处理披萨店的订单。并且披萨店还开了加盟店,有纽约披萨店和芝加哥披萨店。
类图
- 创建者类(抽象工厂类 + 具体工厂实现类)
- 产品类(抽象产品类 + 具体产品实现类)
代码实现
- 抽象工厂类
PizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = null;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
// 抽象的工厂方法
abstract Pizza createPizza(String type);
}
- 具体工厂实现类1
NYPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if(type.equals("prpperoni")) {
pizza = new NYStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new NYStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new NYStyleVeggiePizza();
}
return pizza;
}
}
- 具体工厂实现类2
ChicagoPizzaStore
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStylePizzaStore extends PizzaStore {
@Override
Pizza createPizza(String type) {
Pizza pizza = null;
if(type.equals("cheese")) {
pizza = new ChicagoStyleCheesePizza();
} else if(type.equals("prpperoni")) {
pizza = new ChicagoStylePepperoniPizza();
} else if(type.equals("clam")) {
pizza = new ChicagoStyleClamPizza();
} else if(type.equals("veggie")) {
pizza = new ChicagoStyleVeggiePizza();
}
return pizza;
}
}
- 抽象产品类
Pizza
package cn.edu.nwpu.factoryMethod;
import java.util.ArrayList;
/**
*
* @author yylin
*
*/
public abstract class Pizza {
/*
* 抽象类提供了默认基本做法, 准备工作以特定顺序进行
*/
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
public 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));
}
}
public void bake() {
System.out.println("Bake for 25 minutes at 350");
}
public void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
public void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public String getName() {
return name;
}
}
- 具体产品实现类1
NYStyleCheesePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleCheesePizza extends Pizza {
/*
* 具体产品实现类中可以加入自己的特色,或者覆盖Pizza类中的方法
*/
public NYStyleCheesePizza(){
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
}
- 具体产品实现类2
NYStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStylePepperoniPizza extends Pizza {
}
- 具体产品实现类3
NYStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleClamPizza extends Pizza {
}
- 具体产品实现类4
NYStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class NYStyleVeggiePizza extends Pizza {
}
- 具体产品实现类5
ChicagoStyleCheesePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleCheesePizza extends Pizza {
/*
* 具体产品实现类中可以加入自己的特色,或者覆盖Pizza类中的方法
*/
public ChicagoStyleCheesePizza() {
name = "";
dough = "";
sauce = "";
toppings.add("");
}
@Override
public void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
- 具体产品实现类6
ChicagoStylePepperoniPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStylePepperoniPizza extends Pizza {
}
- 具体产品实现类7
ChicagoStyleClamPizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleClamPizza extends Pizza {
}
- 具体产品实现类8
ChicagoStyleVeggiePizza
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class ChicagoStyleVeggiePizza extends Pizza {
}
- 测试类
PizzaTestDrive
顾客Ethan想要纽约风格的奶酪披萨,顾客Joel想要芝加哥风格的奶酪披萨。
package cn.edu.nwpu.factoryMethod;
/**
*
* @author yylin
*
*/
public class PizzaTestDrive {
public static void main(String[] args) {
// 建立两个不同的披萨店
PizzaStore nyStore = new NYStylePizzaStore();
PizzaStore chicagoStore = new ChicagoStylePizzaStore();
// 顾客Ethan的订单
Pizza pizza = nyStore.orderPizza("cheese");
System.out.println("Ethan ordered a " + pizza.getName() + "\n");
// 顾客Joel的订单
pizza = chicagoStore.orderPizza("cheese");
System.out.println("Joel ordered a " + pizza.getName() + "\n");
}
}
- 测试结果
Preparing NY Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings:
Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in official PizzaStore box
Ethan ordered a NY Style Sauce and Cheese Pizza
Preparing Chicago Style Deep Dish Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings:
Shredded Mozzarella Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Joel ordered a Chicago Style Deep Dish Cheese Pizza
小结
工厂方法模式又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂模式(Polymorphic Factory)。
在工厂方法模式中,父类负责定义创建对象的公共接口,而子类则负责生成具体的对象;
这样做的目的是将类的实例化操作延迟到子类中完成;
即由子类来决定究竟应该实例化(创建)哪一个类。工厂方法模式包含四个角色:
(1)抽象产品是定义产品的接口,是工厂方法模式所创建对象的超类型,即产品对象的共同父类或接口;
(2)具体产品实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,它们之间往往一一对应;
(3)抽象工厂中声明了工厂方法,用于返回一个产品,它是工厂方法模式的核心,任何在模式中创建对象的工厂类都必须实现该接口;
(4)具体工厂是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户调用,返回一个具体产品类的实例。工厂方法模式是简单工厂模式的进一步抽象和推广。
由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。
这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。工厂方法模式:
(1)主要优点:增加新的产品类时无须修改现有系统,并封装了产品对象的创建细节,系统具有良好的灵活性和可扩展性;
(2)缺点在于增加新产品的同时需要增加新的工厂,导致系统类的个数成对增加,在一定程度上增加了系统的复杂性。工厂方法模式适用情况包括:
(1)一个类不知道它所需要的对象的类;
(2)一个类通过其子类来指定创建哪个对象;
(3)将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定。