设计模式之工厂模式
当我们只用new去创建一个类时,我们就在针对这个具体类编程。如果能够不使用具体类,而是针对接口编程,将会对以后扩展有很大方便。
情景:
你是一个披萨店的老板,披萨有很多种类。
我们可能需要这么做
Pizza orderPizza(String type) { Pizza pizza; if (type == A) { pizza = new APizza(); } else if (type == B) { pizza = new BPizza(); } else if (type == C) { pizza = new CPizza(); } ... // 制作披萨订单的其他操作 }
但是以后我们可能会有D,E,F....之列的更多的披萨。我们必须重新修改这份代码。
根据“封装变化”的设计原则,我们需要把创建对象的部分封装起来。
public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("A")) { pizza = new APizza(); } else if (type.equals("B")) { pizza = new BPizza(); } else if (type.equals("C")) { pizza = new CPizza(); } return pizza; } }
这样在制作订单时只需在工厂取一个Pizza就可以了。
public class PizzaStore { SimplePizzaFactory factory; public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; } public Pizza orderPizza(String type) { Pizza pizza; pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
↑ 简单工厂 不是一种设计模式,反而像是一种编程习惯。
现在有很地区要建立披萨店的分店,你希望所有的店都有相同的流程,但是不同的地区会有不同的口味,比如有些地方只吃DEF口味的披萨,也就是说不同地区要有不同的工厂,没有方法把Pizza的制作活动全部局限于PizzaStore类呢?
方法是把createPizza放进PizzaStore类中,并设置成抽象方法,具体实现由每一个地区的子类完成。
public abstract class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza; pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } abstract Pizza createPizza(String type); }
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类创建对象的代码解耦了。
abstract Product factoryMethod(String type)
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
我们最一开始的代码,PizzaStore的实现是依赖于具体Pizza类的,使用工厂模式后,它已经不再依赖于具体的披萨类,也就是减少了对具体类的依赖。
设计原则:依赖倒置原则(Dependency Inverseion Principle)
要依赖抽象,不要依赖具体类。
这个原则说明了,不要让高层组件依赖底层组件,而且,不管高层组件还是底层组件,两者都应该依赖抽象。
使用了工厂方法模式之后,PizzaStore类依赖Pizza这个接口,而具体的APizza,BPizza等具体披萨类也依赖于Pizza这个接口,这就是一种依赖倒置。
………………
现在又有一个新的问题
就是具体Pizza的制作。上面没有写具体披萨的制作过程,默认都是一样的。但是!不同地区用到原料也可能有不同,例如A地产辣椒和B地是不一样的,于是,我们还应该给Pizza写一个原料工厂~~~
首先为工厂定义一个接口
public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Clams createClam(); }
为每一个地区创建一个原料工厂,比如纽约的原料工厂
public class NYPizzaIngredientFactory implements PizzaIngredientFactory { @Override public Dough createDough() { return new ThinCrustDough(); } @Override public Sauce createSauce() { return new MarinaraSauce(); } @Override public Cheese createCheese() { return new ReggianoCheese(); } @Override public Veggies[] createVeggies() { Veggies[] veggies = { new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; return veggies; } @Override public Pepperoni createPepperoni() { return new SlicedPepperni(); } @Override public Clams createClam() { return new FreshClams(); } }
抽象的Pizza类
public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clams; abstract void prepare(); public void bake() { System.out.println("Bake for 25 minutes at 350."); } public void cut() { System.out.println("Cutting the pizza into diagonal slices."); } public void box() { System.out.println("Place pizza in official PizzaStore box."); } void setName(String name) { this.name = name; } String getName() { return name; } public String toString() { return name; } }
具体的Pizza类。每个Pizza的制作流程其实是一样的。只有原料不同,同时原料会和地点有关。为具体的类添加一个原料工厂。
public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public CheesePizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } @Override void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
纽约的PizzaStore类
public class NYPizzaStore extends PizzaStore { @Override Pizza createPizza(String item) { Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if (item.equals("cheess")) { pizza = new ChessPizza(ingredientFactory); } else if (item.equals("veggie")) { pizza = new VeggiePizza(); pizza.setName("New York Style Veggie Pizza."); } // else if .... 还有其他种类的Pizza return pizza; } }
完成~~
我们引入了新类型的工厂,也就是所谓的抽象工厂,来创建披萨家族。
抽象工程模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象类的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体产品中被解耦。
类图
工厂方法模式通过继承,而抽象工厂模式是通过组合实现。
抽象工厂是用来创建一个产品家族的抽象类型,可以把一群相关产品集合起来。