设计模式 -- 抽象工厂
还记得吧?我们已经学完了简单工厂和工厂方法了,今天把“抽象工厂”学习完,那么我们的工厂模式就算学完了。
1、什么是“抽象工厂”?
抽象工厂模式提供一个接口,用语创建相关或依赖对象的家族,而不需要明确指定具体类。这个概念还真是不好理解,没事,我们接着以例子来说明。
还是关于Pizza饼的故事。Pizza的做法(面团+酱汁+芝士)都是相同的,唯一的差别是他们使用的原料。由于各地区Pizza风味不同,原料使用上自然会有差别,有的地区会在原料上偷工减料,这样就会败坏Pizza长年以来的好名声。为了确保各地区Pizza质量,我们决定要建造一个工厂来生产原料,这个工厂将负责创建原料家族中每一种原料,也就是说工厂将需要生产“面团”,“酱汁”,“芝士”等。这样,每一个地区的原料都必须从工厂取得,那么我们就能保证Pizza的质量了。
2、抽象工厂如何实现?
首先,我们需要些一个Pizza原料的工厂接口(PizzaIngredientFactory),这个接口负责创建所有原料。接下来我们需要为每个地区创建一个继承自PizzaIngredientFactory的子类来实现每一种创建方法。然后呢,我们需要将各地区的原料工厂整合进各地区的PizzaStore中。
写到这了,还是让我们先看看代码吧
首先,我们要有一个原料工厂
//抽象原料工厂 public interface PizzaIngredientFactory { Dough createDough(); Sauce createSauce(); Cheese createCheece(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clam createClam(); }
接下来,为各地区创建原料工厂
//纽约地区的原料工厂 public class NYPizzaIngredientFactory:PizzaIngredientFactory { public Dough createDough() { return new ThinCrustDough(); } public Sauce createSauce() { return new MarinaraSauce(); } public Cheese createCheece() { return new ReggianoCheese(); } public Veggies[] createVeggies() { Veggies[] veggies = {new Garlic(),new Onion(),new Mushroom(),new Redpepper()};//注意数组声明方式,[]放在数据类型之后 return veggies; } public Pepperoni createPepperoni() { return new SlicePepperoni(); } public Clam createClam() { return new FreshClam(); } }
//芝加哥地区的原料工厂 public class ChicagoPizzaIngredientFactory:PizzaIngredientFactory { public Dough createDough() { return new ThickCrustDough(); } public Sauce createSauce() { return new PlumTomatoSauce(); } public Cheese createCheece() { return new MazzarellaCheese(); } public Veggies[] createVeggies() { Veggies[] veggies = { new BlackOlives(), new Spinach(), new EggPlant() };//注意数组声明方式,[]放在数据类型之后 return veggies; } public Pepperoni createPepperoni() { return new SlicePepperoni(); } public Clam createClam() { return new FrozenClam(); } }
呵呵,看到这里是不是有点糊涂了?没事,你是看到了那些未知的原料的缘故,我们把他们写出来吧。
以下就是所有原料的类图,你会看到有很多很多原料,但是不用担心,我们只是给每个原料写了一个构造放方法,来表明它是什么原料。这个代码自己完成吧。
有了这些原料类,是不是觉得原料工厂好理解了?再来回顾一下,我们的原料工厂根据各地区的不同需求“生产”原料。那么这些原料何时又如何应用到Pizza身上呢?不要着急,往下走!
先让我们看看Pizza吧?你想把它设计成什么呢?普通类?抽象类?接口?呵呵,我们把它设计成抽象类吧,待会你就就会明白的。
public abstract class Pizza { public string name; public string Name { get { return name; } set { name = value; } } //每个Pizza都持有一组在准备时会用到的原料 public Dough dough;//面团 public Sauce sauce;//酱汁 public Veggies[] veggies;//蔬菜 public Cheese cheese;//奶酪 public Pepperoni pepperoni;//胡椒 public Clam clam;//蛤蛎 //抽象方法,这个方法收集Pizza需要的所有原料,而这些原料当然是来自工厂 public abstract void prepare(); public void bake() { Console.WriteLine("烘烤 25 分钟"); } public void cut() { Console.WriteLine("将Pizza沿对角线切开"); } public void box() { Console.WriteLine("将Pizza装在官方Pizza盒子中"); } }
//奶酪Pizza public class CheesePizza:Pizza { PizzaIngredientFactory pizzaIngredientFactory; public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory) { this.pizzaIngredientFactory = pizzaIngredientFactory; } public override void prepare() { Console.WriteLine("prepare "+Name); dough = pizzaIngredientFactory.createDough(); sauce = pizzaIngredientFactory.createSauce(); cheese = pizzaIngredientFactory.createCheece(); } } //蛤蛎Pizza public class ClamPizza:Pizza { PizzaIngredientFactory pizzaIngredientFactory;//维护自己的工厂 public ClamPizza(PizzaIngredientFactory pizzaIngredientFactory) { this.pizzaIngredientFactory = pizzaIngredientFactory; } public override void prepare() { Console.WriteLine("prepare " + Name); dough = pizzaIngredientFactory.createDough(); sauce = pizzaIngredientFactory.createSauce(); cheese = pizzaIngredientFactory.createCheece(); clam = pizzaIngredientFactory.createClam(); } }
看看这个类图吧:
其实,这个Pizza还可以写很多子类,这里就不多写了。每一个具体的Pizza都要重写prepare方法,根据自己的需要来准备原料。
好了,差不多了,咱们进去PizzaStore看看吧!
//抽象的PizzaStore public abstract class PizzaStore { //请注意这里是一个抽象方法!!! public abstract Pizza createPizza(string item); public Pizza orderPizza(string item) { Pizza pizza; pizza = createPizza(item); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
//重写createPizza方法 public override Pizza createPizza(string item) { ChicagoPizzaIngredientFactory chFactory = new ChicagoPizzaIngredientFactory(); Pizza pizza = null; if (item == "cheese") { pizza = new CheesePizza(chFactory); pizza.Name = "Chicago Style Cheese Pizza"; } if (item == "clam") { pizza = new ClamPizza(chFactory); pizza.Name = "Chicago Style Clam Pizza"; } return pizza; } } public class NYPizzaStore:PizzaStore { public override Pizza createPizza(string item) { NYPizzaIngredientFactory nyFactory = new NYPizzaIngredientFactory(); Pizza pizza = null; if (item == "cheese") { pizza = new CheesePizza(nyFactory); pizza.Name = "New York Style Cheese Pizza"; } if (item == "clam") { pizza = new ClamPizza(nyFactory); pizza.Name = "New York Style Clam Pizza"; } return pizza; } }
看到了吗?每一个地区的PizzaStore里面都重写了createPizza方法,并且在这个方法中都有自己需要的原料工厂,然后根据不同口味的Pizza来成产不同的Pizza,当然,在这个过程中,就会调用Pizza的工厂来生产原料了。
好了,差不多我们该收场了,请你吃Pizza吧,来!
static void Main(string[] args) { //纽约风味的蛤蛎Pizza NYPizzaStore NYPS = new NYPizzaStore(); NYPS.orderPizza("clam"); Console.WriteLine("\n"); //纽约风味的奶酪Pizza NYPS.orderPizza("cheese"); Console.ReadKey(); }
OK!!这就完了,不过我们还是回顾一下吧!
首先,我们引入了原料抽象工厂,来创建Pizza原料家族。 这个工厂是抽象的,必然会有具体的工厂来继承它,那就是具体地区的原料工厂。那么这个工厂是怎么和Pizza或者PizzaStore发生联系的呢?------我们在具体Pizza类中维护了一个工厂!这样每个Pizza都会在prepare的时候调用自己的工厂来生产原料。不过要注意啊,这里维护的都是抽象工厂,因为我们不知道,这种口味的Pizza会在哪种风味的PizzaStore中被点。
然后,我们在具体的PizzaStore中维护了各个地区的原料工厂,这样就能在createPizza()的时候,把这个具体的原料工厂给Pizza,那么Pizza就能使用具体的工厂生产原料了!
在这里,我们还意识到了一点,就是工厂方法潜伏在了抽象工厂里。何时?呵呵,Pizza的prepare()方法和PizzaStore中的createPizza()方法。抽象工厂的任务是定义一个负责创建一组产品的接口,这个接口内部的每个方法都负责创建一个具体产品。同时,我们利用抽象工厂的子类来提供这些具体的做法,所以,在抽象工厂中使用抽象方法很自然。