前言:编程
除了使用new操做符以外,还有更多制造对象的方法。你将了解到实例化这个活动不该该老是公开的进行,也会意识到初始化会形成“耦合”的问题。工厂模式将会从复杂的依赖中帮你脱困。设计模式
当看到“new”,就会想到“具体”,的确也是在实例化一个具体的类,而不是接口。代码绑着具体的类致使代码更脆弱,更缺少弹性。当有一群相关的具体类时,一般会有以下代码:框架
Duck duck; If(picnic) duck=new MallardDuck(); else if(hunting) duck=new DecoyDuck(); else if(inBathTub) duck=new RubberDuck();
这样的代码一旦有变化或者扩展,就必须从新修改此段代码进行检查和修改,这样的修改容易致使系统的维护和更新更难,也更容易犯错。ide
针对接口编程,能够隔离掉之后系统可能发生的一大堆改变,由于针对接口而写,能够经过多态,与任何新类实现该接口。当代码使用具体类时,一旦加入新的一些具体类就必须改变代码。这就违背了“对修改关闭”的原则。为了解决这样的问题,咱们能够经过“找出变化,隔离并封装变化“的方式来解决。this
现实场景:设计
披萨店生产披萨,当须要生产更多类型的披萨的时候,压力来自于如何增长更多的披萨类型。code
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; if (pizzaType.Equals("cheese")) pizza = newCheesePizza(); else if(pizzaType.Equals("greek")) pizza = newGreekPizza(); else if(pizzaType.Equals("pepperoni")) pizza = newPepperoniPizza(); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
如同开始是讲的那样,要新增新的pizza,就须要修改这段代码,修改以下:对象
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; if(pizzaType.Equals("cheese")) pizza = newCheesePizza(); else if(pizzaType.Equals("greek")) pizza = newGreekPizza(); else if(pizzaType.Equals("pepperoni")) pizza = newPepperoniPizza(); else if(pizzaType.Equals("clam")) //新增的pizza类型 pizza = newCalmPizza(); else if(pizzaType.Equals("veggie"))//新增的pizza类型 pizza = newVeggiePizza(); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
根据咱们上边提到的“将变化抽离并封装“的原则,咱们能够将建立pizza实例这一块给抽离出来,由于这块后边可能会新增一些别的pizza类型,由一个对象来专职建立pizza对象。咱们称这个新对象为”工厂“。代码以下:继承
public class SimplePizzaFactory { public PizzaCreatePizza(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; } }
这样作的好处在于,把建立pizza对象的方法包装成一个类,当之后实现改变时,只须要修改这个类便可。与此同时咱们还能够把生成其余类的方法也放在这个简单的工厂中。接口
这样咱们生成pizza类的代码就变成以下的样子:
public class Pizza { Pizza OrderPizza(stringpizzaType) { Pizza pizza; SimplePizzaFactorysimplePizzaFactory = new SimplePizzaFactory();//生成pizza pizza =simplePizzaFactory.CreatePizza(pizzaType); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
通过这样一系列的修改,咱们的Pizza的类图就变成以下的样子:
虽然咱们一直在说简单的工厂,但事实上简单工厂并非一个设计模式,更像是一种编程习惯。这里讲简单的工厂,主要是为了引出下面的两个重量级的模式,它们都是工厂。
在1中,咱们经过简单的工厂解决了生产不一样披萨的问题,可是,若是有新的加盟店加盟进来,如何解决不一样加盟店的区域差别、质量问题呢?或许,咱们能够像1中那样利用简单的工厂,对应不一样地区的加盟店建立不一样的工厂。
这样作致使的另外一个问题就是,不一样的加盟店披萨的制做流程、方法可能不一样,如何才能把加盟店和建立披萨捆绑在一块儿的同时又保持必定的弹性?
咱们能够把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 PizzaCreatePizza(string pizzaType);//把工厂对象移到该方法中,该方法为抽象方法 }
如今咱们有了PizzaStore超类,让各个不一样地域的加盟店继承此超类便可。具体的类图以下:
本来是由一个对象复制全部具体类的实例化,如今经过对PizzaStore作一些转变,变成由一群子类负责实例化。
工厂方法用来处理对象的建立,并将这样的行为封装在子类中。这样客户程序中关于超类的代码就和子类对象建立代码解耦。
public abstract class PizzaStore { public Pizza OrderPizza(stringtype) { Pizza pizza = CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } public abstract Pizza CreatePizza(stringtype);//抽象出建立Pizza的工厂方法,由子类实现该方法并建立具体的Pizza }
public class MYPizzaStore:PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza=null; switch(type) { case "cheese": pizza = new NYStyleCheesePizza(); break; case "veggie": pizza=new NYStyleVeggiePizza(); break; case "clam": pizza=new NYStyleClamPizza(); break; case "pepperoni": pizza=new NYStylePepperoniPizza(); break; } return pizza; } }
public abstract class Pizza { public string name; public string dough; public string sauce; public ArrayList toppings = newArrayList(); public void Prepare() { System.Console.WriteLine("Preparing" + name); System.Console.WriteLine("Tossingdough..."); System.Console.WriteLine("Addingsauce.."); System.Console.WriteLine("Addingtoppings: "); for(int i = 0; i < toppings.Count; i++) { System.Console.WriteLine(" "+ toppings[i]); } } public void Bake() { System.Console.WriteLine("Bakefor 25 minutes at 350"); } public void Cut() { System.Console.WriteLine("Cuttingthe pizza into diagonal slices"); } public void Box() { System.Console.WriteLine("Placepizza in official PizzaStore box"); } public string GetName() { return name; } }
public class NYStyleCheesePizza : Pizza { public NYStyleCheesePizza() { name = "NY StyleSauc and Cheese Pizza"; dough="Thin Crust Dough"; sauce="Marinara Sauce"; toppings.Add("GratedReggiano Cheese"); } }
全部工厂模式都用来封装对象建立,工厂方法模式经过让子类决定该建立的对象是什么,来达到将对象建立的过程封装的目的。
工厂方法模式定义了一个建立对象的接口,但由子类决定要实例化的类是哪个,工厂方法让类把实例化推迟到子类。
建立者(Creator)类
产品类
简单工厂和工厂方法之间的差别?
简单工厂是在一个地方把全部的事都处理完了,然而工厂方法倒是建立一个框架,让子类决定要如何实现。简单工厂的作法,能够将对象的建立封装起来,可是简单工厂不具有工厂方法的弹性,由于简单工厂不能变动正在建立的产品。
要依赖抽象,不要依赖具体类
不能让高层组件依赖底层组件,并且无论高层、底层组件,二者都应该依赖于抽象。
如何避免违反依赖倒置原则:
l 变量不能够持有具体类的引用。
若是使用new,则会持有具体类的引用,能够用工程来避开这样的作法
l 不要让类派生自具体类。
若是派生自具体类,你就会依赖具体类(请派生自一个抽象类或接口)
l 不要覆盖基类中已实现的方法。
若是覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由全部的子类共享。
回到上文的Pizza店,如今有新的需求,想要确保每家加盟店使用高质量的材料,打算建立一家生产原料的加工厂,并将原料送到各个加盟店。这个工厂负责建立原料家族中的每一种原料,工厂须要生产面团、酱料、芝士等。先为工厂定义一个接口,该接口负责全部原料:
public interface PizzaIngredientFactory { Dough CreateDough(); Sauce CreateSauce(); Cheese CreateCheese(); Veggies[] CreateVeggies(); Pepperoni CreatePepperoni(); Clams CreateClam(); }
public class NYPizzaIngredientFactory : 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 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 { public string name; public Dough dough; public Sauce sauce; public Veggies[] veggies; public Cheese cheese; public Pepperoni pepperoni; public Clams clam; public ArrayList toppings = newArrayList(); public abstract void Prepare();//把Prepare()方法声明成抽象,在这个方法中,咱们须要收集Pizza所需的原材料,而这些原材料来自原料工厂。 public void Bake() { System.Console.WriteLine("Bakefor 25 minutes at 350"); } public void Cut() { System.Console.WriteLine("Cuttingthe pizza into diagonal slices"); } public void Box() { System.Console.WriteLine("Placepizza in official PizzaStore box"); } public string GetName() { return name; } }
public class NYStyleCheesePizza : Pizza { PizzaIngredientFactory ingredientFactory; public NYStyleCheesePizza(PizzaIngredientFactoryingredientFactory)//制做Pizza须要工厂提供原材料,
因此每一个pizza类都须要从构造器中获得一个工厂,并将工厂存储在变量中 { this.ingredientFactory =ingredientFactory; name = "NY StyleSauc and Cheese Pizza"; toppings.Add("GratedReggiano Cheese"); } public override void Prepare() { System.Console.WriteLine("Preparing" + name); dough = ingredientFactory.CreateDough(); sauce = ingredientFactory.CreateSauce(); cheese = ingredientFactory.CreateCheese(); } }
public class MYPizzaStore:PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza=null; PizzaIngredientFactory ingredientFactory= new NYPizzaIngredientFactory(); switch(type) { case "cheese": pizza = new NYStyleCheesePizza(ingredientFactory); break; case "veggie": pizza=new NYStyleVeggiePizza(ingredientFactory); break; case "clam": pizza=new NYStyleClamPizza(ingredientFactory); break; case "pepperoni": pizza=new NYStylePepperoniPizza(ingredientFactory); break; } return pizza; } }
经过这一系列的操做,咱们引入了新类型的工厂,也就是所谓的“抽象工厂”,来建立pizza原来家族。经过抽象工厂所提供的接口建立产品家族,利用这个接口书写代码,咱们的代码将从实际工厂解耦,以便在不一样上下文中实现各式各样的工厂,制造出各类不一样的产品。
抽象工厂模式提供一个接口,用于建立相关或依赖对象家族,并且不须要致命具体类。
抽象工厂模式容许客户使用抽象的接口来建立一组相关的产品,而不须要知道实际产出的具体产品是什么,客户就从具体的产品中被解耦。
抽象工厂和工厂方法都是负责建立对象。
抽象工厂是经过对象的组合
定义了一个建立对象的接口,但因为子类决定要实例化的类是哪个,工厂方法让类把实例化推迟到子类。
因此利用工厂方法建立对象时,须要扩展一个类,并覆盖它的工厂方法。
整个工厂方法模式,只不过就是经过子类来建立对象,这种作法,客户只须要知道他们所使用的抽象类型就能够了,而由子类负责决定具体类型。将客户从具体类型中解耦。
工厂方法是继承。
抽象工厂方法是将一群相关的产品集合起来,用于建立相关或依赖对象的家族,而不须要明确指定具体类。