所有工厂模式都用来封装对象的创建。
工厂方法模式
举例中国版披萨:有个披萨店,卖不同种类的披萨,假定有蔬菜、水果披萨。每一个种类披萨有湖南和北京风味之分。
public abstract class PizzaStore
{
public abstract Pizza CreatePizza(PizzaType pizzaType);
public Pizza OrderPizza(PizzaType pizzaType)
{
Pizza pizza = CreatePizza(pizzaType);
pizza.Prepare();
pizza.Bake();
pizza.Box();
return pizza;
}
}
public class HunanPizzaStore : PizzaStore
{
public override Pizza CreatePizza(PizzaType pizzaType)
{
Pizza pizza=null;
switch (pizzaType)
{
case PizzaType.Fruit:
pizza = new HunanStyleFruitPizza();
break;
case PizzaType.Vegetalbe:
pizza = new HunanStyleVegetablePizza();
break;
}
return pizza;
}
}
工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。通常由创建类(PizzaStore)和产品类(Pizza)组成。正式定义为:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。在上面的创建者(类)中,CreatePizza被称为工厂方法。 测试使用代码如下:
PizzaStore pizzaStore = new HunanPizzaStore();
pizzaStore.OrderPizza(PizzaType.Fruit);
当程序中直接实例化一个对象时,就是在依赖它的具体类。依赖性就很高,代码应该减少这种依赖,解耦,OO设计原则中“依赖倒置原则”(Dependency Inversion Principle)描述的就是要依赖抽象,不要依赖具体类。与“针对接口编程,不针对实现编程”类似,但这里强调“抽象”。不能让高层组件依赖低层组件,而且,不管高层组件和低层组件,都应该依赖抽象。
在上面例子中,PizzaStore是“高层组件”,而比萨实现是“低层组件”。把低层组件抽象出来,工厂方法刚好能派上用场。在上面例子中,高层组件PizzaStore和低层组件都依赖了Pizza抽象。通常高层组件依赖低层组件,但工厂方法却让低层组件依赖了高层的抽象Pizza对象,这就是“倒置”的现象。在低层组件的实现中,工厂方法将这些具体的类取出PizzaStore,各种不同种类的Pizza就只能依赖一个抽象,同时比萨店也依赖这个抽象,倒置就实现了。
抽象工厂模式
在上面的例子中,假定各种不同风味的Pizza就是制作Pizza时原料的不同,例如湖南风味酱汁少、辣椒多,北京风味酱汁多、不放辣(仅举例说明,不深究合理性)。不用单独创建各种类的 HunanStylePizza 或BeijingStylePizza 。此时改用创建不同的原料来制作各种风味的Pizza。
首先创建一个原料接口,两个原料工厂,分别生产湖南风味和北京风味的原料:
public interface IPizzaIngredientFactory
{
Vegetable[] CreateVegetable();
Fruit[] CreateFruit();
Sauce CreateSauce();
Pepper CreatePepper();
}
public class HunanIngredientFactory : IPizzaIngredientFactory
{
public Fruit[] CreateFruit()
{
return new Fruit[] { new Orange(),new Apple() };
}
public Pepper CreatePepper()
{
return new Pepper() { Weight=2.5f, };
}
public Sauce CreateSauce()
{
return new HunanPizzaSauce();
}
public Vegetable[] CreateVegetable()
{
return new Vegetable[] { new Onion(),new Tomato(),new Potato()};
}
}
public class BeijingIngredientFactory : IPizzaIngredientFactory
{
public Fruit[] CreateFruit()
{
return new Fruit[] { new Orange(), new Apple(),new Banana() };
}
public Pepper CreatePepper()
{
return new Pepper() { Weight = 0 };
}
public Sauce CreateSauce()
{
return new BeijingPizzaSauce();
}
public Vegetable[] CreateVegetable()
{
return new Vegetable[] { new Onion(), new Tomato() };
}
}
接着将Pizza抽象类中的Prepare定义为抽象方法,目的为了让工厂来完成制作披萨的原料准备工作。
public abstract class Pizza
{
public string Name { get; set; }
public abstract void Prepare();
public void Bake()
{
Console.WriteLine("{0} 烘焙25 至30 分钟",Name);
}
public void Box()
{
Console.WriteLine("{0}, 装盒, 完工",Name);
}
}
把工厂方法模式中的BeijingStyleVegetablePizza和HunanStyleVegetablePizza 合并成一类VegetablePizza,让工厂来处理不同的风味。同水果种类的披萨也合并改造。
public class VegetablePizza : Pizza
{
IPizzaIngredientFactory ingredientFactory;
public VegetablePizza(IPizzaIngredientFactory factory)
{
this.ingredientFactory = factory;
}
public override void Prepare()
{
Console.WriteLine("准备中: {0}",Name);
Vegetable[] vegetables= ingredientFactory.CreateVegetable();
foreach (var veg in vegetables)
{
Console.WriteLine(" >>添加:{0}", veg.Name);
}
Pepper pepper = ingredientFactory.CreatePepper();
if (pepper.Weight > 0)
{
Console.WriteLine(" >>添加辣椒量:{0}g", pepper.Weight);
}
else
{
Console.WriteLine(" >>不放辣");
}
Sauce sauce = ingredientFactory.CreateSauce();
Console.WriteLine(" >>添加酱料:{0}",sauce.Name);
Console.WriteLine("{0} 披萨调料好了",Name);
}
}
测试生产披萨过程:
PizzaStore pizzaStore = new HunanPizzaStore();
pizzaStore.OrderPizza(PizzaType.Fruit);
Console.WriteLine();
pizzaStore.OrderPizza(PizzaType.Vegetalbe);
Console.WriteLine();
pizzaStore = new BeijingPizzaStroe();
pizzaStore.OrderPizza(PizzaType.Vegetalbe);
类的全局关系图
上面的设计被定义为“抽象工厂模式”:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么。如此,客户就从具体的产品中被解耦。
工厂方法与抽象工厂的异同:
都是用来创建对象,将客户从具类型中解耦。工厂方法通过继承有子类来创建对象,客户只需要知道他们所使用的抽象类型就可以了。抽象工厂通过对象的组合来完成,它提供一个用来创建一个产品家族的抽象类型,把一群相关的产品集合起来。当需要创建产品家族并把相关产品集合起来时,可以使用抽象工厂模式,如果目前还不知道将来需要实例化哪些具体类时,可以使用工厂方法模式。