[设计模式]工厂模式——抽象工厂
简介
根据《head first 设计模式》所述,工厂模式共有三种:静态工厂方法、工厂方法、抽象工厂。本文介绍抽象工厂。
1、定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体类。
2、满足的OO原则——依赖倒置原则:要依赖抽象,不要依赖具体类。
3、模式结构:工厂类(抽象工厂和具体工厂)和产品类(抽象产品和具体产品)
4、具体实现(完整代码):
同样是做披萨系统,工厂方法是以披萨店为抽象进行设计,这样一个地区的披萨风味可以作为一个具体创建者类来实现,我们可以随意增加一个披萨风味,但是这样的话,可扩展性还是不够,因为如果新出现的一个披萨风味用了不同的原料的话,我们依然需要更改抽象产品类(Pizza类)以及具体产品类进行扩充。而抽象工厂则是以原料抽象出来作为工厂进行设计,这样我们需要创建原料类(抽象工厂类),再选择不同的原料进行生产披萨即可,如果原料有所增加或者减少的话,我们只要更改原料类即可,同时这样的更改不会影响到其他的类。(而工厂方法中,如果减少原料的话,可能要更改一批涉及此原料的具体产品类,这是一个比较大的改动)
- 抽象工厂类:定义了一个接口,所有的具体工厂都必须实现此接口,所有的具体工厂都必须实现此接口,这个接口包含一组方法用来生产产品。
public interface PizzaIngredientFactory{ public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); }
- 具体工厂类:实现不同的产品家族,要创建一个产品,客户只要使用其中的一个工厂而完全不需实例化任何产品对象。
//制作芝加哥披萨的原料工厂 public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{ //面团 public Dough createDough(){ return new ThickCrustDough(); } //酱料 public Sauce createSauce(){ return new PlumTomatoSauce(); } //芝士 public Cheese createCheese(){ return new MozzarellaCheese(); } //蔬菜 public Veggies[] createVeggies(){ Veggies veggies[] = {new Spinach(),new BlackOlives(),new EggPlant()}; return veggies; } //意式腊肠 public Pepperoni createPepperoni(){ return new SlicedPepperoni(); } //蛤蜊 public Clams createClam(){ return new FrozenClams(); } } //制作纽约披萨的原料工厂 public class NYPizzaIngredientFactory implements 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 Garlic(),new Onion(),new Mushroom(),new RedPepper()}; return veggies; } //意式腊肠 public Pepperoni createPepperoni(){ return new SlicedPepperoni(); } //蛤蜊 public Clams createClam(){ return new FreshClams(); } }
- 抽象产品类:
//原料:面团 public interface Dough{ public String toString(); } //原料:意式腊肠 public interface Pepperoni{ public String toString(); }
- 具体产品类:
//面团:厚面团 public class ThickCrustDough implements Dough{ public String toString(){ return "Thick Crust Dough"; } } //面团:薄面团 public class ThinCrustDough implements Dough{ public String toString(){ return "Thin Crust Dough"; } } //意式腊肠:腊肠片 public class SlicedPepperoni implements Pepperoni{ public String toString(){ return "Sliced Pepperoni"; } }
- 客户端:客户的代码中只需设计抽象工厂,运行时将自动使用实际的工厂。
//纽约风味的披萨饼 public class NYPizzaStore extends PizzaStore{ protected Pizza createPizza(String item){ Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory(); if(item.equals("cheese")){ pizza = new CheesePizza(ingredientFactory); pizza.setName("New York Style Cheese Pizza"); }else if(item.equals("veggie")){ pizza = new VeggiePizza(ingredientFactory); pizza.setName("New York Style Veggie Pizza"); }else if(item.equals("clam")){ pizza = new ClamPizza(ingredientFactory); pizza.setName("New York Style Clam Pizza"); }else if(item.equals("pepperoni")){ pizza = new PepperoniPizza(ingredientFactory); pizza.setName("New York Style Pepperoni Pizza"); } return pizza; } } //芝加哥风味的披萨饼 public class ChicagoPizzaStore extends PizzaStore{ protected Pizza createPizza(String item){ Pizza pizza = null; PizzaIngredientFactory ingredientFactory = new ChicagoPizzaIngredientFactory(); if(item.equals("cheese")){ pizza = new CheesePizza(ingredientFactory); pizza.setName("Chicago Style Cheese Pizza"); }else if(item.equals("veggie")){ pizza = new VeggiePizza(ingredientFactory); pizza.setName("Chicago Style Veggie Pizza"); }else if(item.equals("clam")){ pizza = new ClamPizza(ingredientFactory); pizza.setName("Chicago Style Clam Pizza"); }else if(item.equals("pepperoni")){ pizza = new PepperoniPizza(ingredientFactory); pizza.setName("Chicago Style Pepperoni Pizza"); } return pizza; } }
5、抽象工厂优点:可以把一群相关的产品集合起来
6、抽象工厂缺点:增加新种类的产品比较困难。
7、工厂方法和抽象工厂的区别:
- 工厂方法使用类,而抽象工厂使用对象
- 工厂方法使用继承来创建对象,而抽象工厂通过对象的组合来创建对象。
注:二者都负责将客户从具体类型中解耦,但方法不同:工厂方法创建对象,需要扩展一个类,并覆盖它的工厂方法;而抽象工厂提供一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法,要想使用这个工厂,必须先实例化它,然后将它传入一些针对抽象类型所写的代码中。
参考资料
[1] head first 设计模式