5 简单工厂、工厂、抽象工厂
一 简单工厂
将代码中负责根据不同情况创建不同类的if else 或者switch case语句抽提出来就构成了简单工厂,即简单工厂专门负责类的生产。
比如,head first中举的pizza例子,用SamplePizzaFactory负责根据客户的选择创建ChessPizza、GreekPizza等不同种类的Pizza。
class SampleFactory { public Pizza CreatePizza(string type) { Pizza pizza; if (type == "ChessPizza") { pizza = new ChessPizza(); } else if (type == "GreekPizza") { pizza = new GreekPizza(); } else { throw new Exception(); } return pizza; } }
UML如下:
这里传递type便是客户做决定的过程。
简单工厂模式严格来说不能算作设计模式,而是一种编程习惯,但却是一种值得采纳的习惯,而且后续工厂模式和抽象工厂模式的代码实现细节也都离不开这种编程习惯。
二 工厂模式
工厂模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
还是关于Pizza的例子,因为不同地区人们的口味不同,同样的ChessPizza在不同的区域会有不同的做法,比如纽约的PizzaStore提供薄饼、芝士少的ChessPizza,但芝加哥的PizzaStore提供的是厚饼、芝士多的ChessPizza,同样GreekPizza等其他Pizza也有区域性差异。
为了应对这种情况,我们可以让NewYorkPizzaStore和ChicagoPizzaStore继承统一的PizzaStore,父类PizzaStore除了自带OrderPizza方法,还增加了抽象方法CreatePizza,PizzaStore作为一种规范存在的同时,可以供子类采用不同的方式实现,这样就满足了Pizza的区域性差异。
代码如下:
//各种PIZZA public abstract class Pizza { public string name; public void Prepare() { Console.WriteLine("Preparing " + name); Console.WriteLine("Tossing dough"); Console.WriteLine("Adding sauce"); } public void Bake() { Console.WriteLine("Baking"); } public void Cut() { Console.WriteLine("Cut"); } } class NYChessPizza : Pizza { public NYChessPizza() { name = "NYChessPizza"; } } class ChicagoChessPizza : Pizza { public ChicagoChessPizza() { name = "ChicagoChessPizza"; } } class NYClamPizza : Pizza { public NYClamPizza() { name = "NYClamPizza"; } } class ChicagoClamPizza : Pizza { public ChicagoClamPizza() { name = "ChicagoClamPizza"; } } //PizzaStore public abstract class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza; pizza = CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); return pizza; } protected abstract Pizza CreatePizza(string type); } public class NYPizzaStore : PizzaStore { protected override Pizza CreatePizza(string type) { Pizza pizza; switch (type) { case "chess": pizza = new NYChessPizza(); break; case "clam": pizza = new NYClamPizza(); break; default: throw new Exception(); } return pizza; } } public class ChicagoPizzaStore : PizzaStore { protected override Pizza CreatePizza(string type) { Pizza pizza; switch (type) { case "chess": pizza = new ChicagoChessPizza(); break; case "clam": pizza = new ChicagoClamPizza(); break; default: throw new Exception(); } return pizza; } } //调用代码 PizzaStore ps = new NYPizzaStore(); ps.OrderPizza("chess");
UML类图:
使用工厂模式后,PizzaStore和Pizza类都是多态的,不同的工厂和不同的Pizza种类可以随意组合。另一方面,PizzaStore中除了CreatePizza方法还有OrderPizza方法等,CreatePizza方法有地域性差异,需要被子类重写,但其余的方法可以在不同地区的PizzaStore通用,这样增加了代码的复用。而这些通用的方法可以不必知道具体要处理的是哪块区域的PizzaStore生产的Pizza,这也就是工厂方法模式定义中所说的“由子类决定要实例化的类是哪一个”。
依赖导致原则
Dependency Inversion Principle 要依赖抽象,不要依赖具体类。
HeadFirst提到这个听起来类似“针对接口编程,不针对实现编程”,经这儿一点,就更糊涂了。不过,依赖导致与接口依赖的侧重点不同,依赖导致原则除了体现了接口依赖之外,更强调层次之间的关系,这个有待加深体会。
就pizza的例子来说,PizzaStore是高层组件,具体的Pizza是底层组件。按照一般的思路就会是PizzaStore依赖具体Pizza类,因为PizzaStore要拿到具体Pizza类并提供给客户。结构会是这样的:
这种极度依赖的结构对维护来说简直是噩梦,因为任何一个Pizza具体类发生变化,都有可能引起PizzaStore的变化,此外还有可能有新的Pizza类型加入。(想想自己写的代码,都是这种类型的:( )
如果按照依赖倒置原则,可以从NewYorkChessPizza、ChicagoGreekPizza中提取出Pizza抽象类,Pizza抽象类包含了Pizza通用的方法和属性,并让子类重写CreatePizza这个方法。PizzaStore只需要知道Pizza这个抽象类,不管是新增Pizza类型还是改变原有Pizza的做法,只要它们继承了Pizza、按照Pizza抽象类的规范来做,就可以被自然报包含到这个体系中,可以被PizzaStore出售。
如下,底层的Pizza具体类依赖更高一层的Pizza抽象类。
简单工厂模式、工厂模式便很好地应用了依赖倒置的原则。简单工厂模式做了一次抽象,而工厂模式做了两次抽象(Pizza抽象类与PizzaStore抽象类)。
避免违反依赖倒置原则
HeadFirst给出了如下建议:
变量不可以持有具体类的引用;
不要让类派生自具体类;
不要覆盖基类中已实现的方法。
三 抽象工厂
定义:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
到了抽象工厂这一块,被HeadFirst的例子绕晕了,有时pizza又是pizza调理的,后来结合大话里的换DB的例子才弄明白,原来HeadFirst中光调料这一块就是一个自成一体的抽象工厂例子。
具体结构是这样的:
Sauce和Chess都有很多种,NewYork的Pizza使用一种调料组合,Chicago的Pizza使用另一种调料组合。初学设计模式,Pizza加调料这样确实不好理解啊,如果用Pizza店的Pizza和意面会不会更合适一点。
在Pizza调料的例子中,用调料工厂PizzaIngredientFactory来创建Pizza所需的调料“家族”。在具体的代码中,依赖接口编程,与实际的调料工厂以及Pizza原料解耦,这样便可以方便得制造不同Pizza需要的原料家族。
代码如下:
//调料Chess和Sauce public abstract class Sauce { } public class MarinaraSauce : Sauce { public MarinaraSauce() { Console.WriteLine("Ingredient MarinaraSauce"); } } public class BruschettaSauce : Sauce { public BruschettaSauce() { Console.WriteLine("Ingredient BruschettaSauce"); } } public abstract class Chess { } public class ReggianoChess : Chess { public ReggianoChess() { Console.WriteLine("Ingredient ReggianoChess"); } } public class GoatChess : Chess { public GoatChess() { Console.WriteLine("Ingredient GoatChess"); } } //调料工厂 interface PizzaIngredientFactory { Sauce CreateSauce(); Chess CreateChess(); } public class NYPizzaIngredientFacotry : PizzaIngredientFactory { public Sauce CreateSauce() { return new MarinaraSauce(); } public Chess CreateChess() { return new ReggianoChess(); } } public class ChicagoPizzaIngredientFacotry : PizzaIngredientFactory { public Sauce CreateSauce() { return new BruschettaSauce(); } public Chess CreateChess() { return new GoatChess(); } } //调用代码 PizzaIngredientFactory pif = new ChicagoPizzaIngredientFacotry(); pif.CreateChess(); pif.CreateSauce();
个人理解:抽象工厂是建立在工厂模式的基础上的,比如,在Pizza调料的例子中,工厂模式可以处理一种调料,但多种调料的处理,即所说的调料家族,便需要用抽象工厂了。所以抽象工厂建立在比工厂模式更高一层的抽象上,而抽象工厂的内部就是工厂模式。