定义
"工厂模式"分为三类:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
简单工厂模式可以帮助开发人员创建不同类型的对象,将对象创建交给专门的工厂类处理,而不是直接将对象实例化。
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化哪一个。工厂方法模式把实例化推迟到子类中。
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
例子均来自《HEAD First设计模式》披萨店
简单工厂模式
当我们在进行对象的创建时,可能会需要在很多对象之间进行选择。例如在进行初始化操作时,需要初始化很多东西,导致代码逻辑比较长,在后续新增需求的时候可能会发生修改。例如披萨店中,可能需要不同的披萨类型:
Pizza pizza;
if (pizzaType.Equals("cheese"))
pizza = newCheesePizza();
else if(pizzaType.Equals("greek"))
pizza = newGreekPizza();
else if(pizzaType.Equals("pepperoni"))
pizza = newPepperoniPizza();
为了防止对这块代码的修改,可以选择将这一块对象创建的代码抽离出来,封装进一个简单披萨工厂:
public class SimplePizzaFactory
{
public createPizza(string pizzaType)
{
Pizza pizza = null;
if(pizzaType.Equals("cheese"))
pizza = newCheesePizza();
else if (pizzaType.Equals("pepperoni"))
pizza = newPepperoniPizza();
else if(pizzaType.Equals("clam"))
pizza = newCalmPizza();
else if(pizzaType.Equals("veggie"))
pizza = newVeggiePizza();
return pizza;
}
}
这个SimplePizzaFactory的作用就是帮助客户创建披萨。这样在进行修改时只需要修改这个类就可以了。因此,披萨店逻辑可以这样写:
public class PizzaStroe
{
SimplePizzaFactory simplePizzaFactory;
Public Pizza(SimplePizzaFactory sp)
{
this.simplePizzaFactory = sp;
}
Pizza OrderPizza(string type)
{
Pizza pizza;
pizza = simplePizzaFactory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
类图如下所示:
其实工厂模式并不是一种真正的设计模式,反而更像是一种编程习惯。
工厂方法模式
在披萨业务扩展中,可能会有更多的加盟店,这些店对于披萨的处理可能并不相同。此时如果把OrderPizza()放在SimplePizzaFactory中,可能弹性不够,比如不同店里面对于披萨的切、烤等工序不同。
可以把CreatePizza()方法放回到PizzaStore中,但是要把它设置为抽象方法,然后为每一个区域加盟店创建一个PizzaStore的子类。
这样代码就变成了:
public abstract class PizzaStore
{
public PizzaOrderPizza(string pizzaType)
{
Pizza pizza = createPizza(pizzaType);
pizza.Prepare();
pizza.Bake();
pizza.Cut();
pizza.Box();
return pizza;
}
public abstract createPizza(string pizzaType);//把工厂对象移到该方法中,该方法为抽象方法
}
这样在建立纽约的披萨店的时候就可以使用:
public class NYPizzaStore extends PizzaStore
{
public createPizza(string pizzaType)
{
if(pizzaType.equals("cheese"))
{
return new NYCheesePizza();
}else if()
{//...}
}
}
这样就可以让子类做决定,子类负责定义自己的createPizza()方法。
因此,工厂方法的核心是:
abstract Product factoryMethod(String type)
工厂方法模式的结构如下图所示(图源:工厂模式):
可以看到,一个orderPizza()方法和工厂方法联合起来,就可以成为这样一个框架,这可以看做是一个平行的类层级。
对于简单工厂和工厂方法之间的关系,简单工厂把所有的事情在一个地方都处理了,工厂方法中将这个下放给了PizzaStore类。
依赖倒置原则
在这种披萨店里面,依赖会很多,例如在创建披萨的函数中,会依赖许许多多的披萨类型,如纽约芝士披萨,芝加哥菜披萨等等,PizzaStore会依赖所有的披萨对象,如果有修改需要修改很多地方。
在代码中应该减少依赖,面向对象设计原则:
依赖倒置原则(Dependency Inversion Principle)
要依赖抽象,不要依赖具体类
这就意味着:不能让高层组件依赖底层组件,而且不管高层、底层组件,两者都应该依赖于抽象。
例如在这个披萨店的例子中,不应该让高层的披萨店依赖底层的披萨,而是应该让披萨依赖于抽象披萨接口,披萨店依赖于披萨接口。
Q:如何避免违反依赖倒置原则?
- 变量不可以持有具体类的引用
如果使用new,则会持有具体类的引用,可以用工厂来避开这样的做法 - 不要让类派生自具体类
如果派生自具体类,就会依赖具体类(可以派生自一个抽象类或接口) - 不要覆盖基类中已实现的方法
如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。
抽象工厂模式
如果在未来想新增一个原料工厂,为所有的加盟店提供原料,每个地方所采用的的原料是不同的。
首先定义一个原料工厂接口,工厂需要生产面团、酱料、芝士等:
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
那么对于纽约的原料工厂,可以这样实现:
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();
}
}
那么对于一个抽象披萨类:
public abstract class Pizza {
String name;
String dough;
String 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;
}
public String getName(){
return name;
}
}
那么对于芝士披萨,可以定义为:
public class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
//作Pizza需要工厂提供原材料,构造函数中获得一个工厂,并把工厂存在一个变量中
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
void prepare(){
System.out.println("Prepareing "+name);
dough=ingredientFactory.createDough();
sauce=ingredientFactory.createSauce();
cheese=ingredientFactory.createCheese();
}
}
那么纽约披萨店就可以这样实现:
public class NYPizzaStore extends PizzaStore{
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;
}
}
抽象工厂模式(Abstract Factory Pattern)
提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
工厂方法模式和抽象工厂模式对比:抽象工厂模式可以更方便的创建出各种各样的披萨工厂,将代码从实际的工厂中解耦,客户只需要使用其中一个工厂,而不需要实例化任何产品对象,如奶酪对象、面饼对象。工厂方法模式采用继承和子类来决定要创建哪个对象;抽象工厂模式使用组合将创建对象的任务委托给其他类。
参考
工厂模式
关于三种工厂模式的总结
Head First 设计模式之工厂模式(Factory Pattern)
工厂模式——Head First
漫画:设计模式之 “工厂模式”
工厂模式