我们有一个 Pizza 店,会根据顾客的要求制作不同类型的 Pizza。目前有三种不同类型的 Pizza:「Cheese & Greek & Pepperoni」Pizza
制作每种类型的 Pizza 时,都会经历 4 个步骤:前期准备「Prepare」、烘焙「Bake」、切「Cut」、装盒「Box」
下面先给出对应的类图:
接着给出代码:
// 抽象类 Pizzaabstract public class Pizza { String name; public String getName() { return name; } // 准备 public void prepare() { System.out.println("准备 " + name); } // 烘焙 public void bake() { System.out.println("烘焙 " + name); } // 切 public void cut() { System.out.println("切 " + name); } // 装盒 public void box() { System.out.println("装盒 " + name); }}// 抽象类 Pizza 的一个实现类 CheesePizza (其它同理)public class CheesePizza extends Pizza { public CheesePizza() { this.name = "Cheese Pizza"; }}// PizzaStore 类public class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza; if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("greek")) { pizza = new GreekPizza(); } else if (type.equals("pepperoni")) { pizza = new Pepperoni(); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } // 测试代码 public static void main(String[] args) { PizzaStore pizzaStore = new PizzaStore(); System.out.println("我们准备了一个 Cheese Pizza !"); Pizza pizza = pizzaStore.orderPizza("cheese"); }}问题:当后续需要添加新的 Pizza 类型或者删去已有的 Pizza 类型,这时就需要修改已有代码的逻辑,违背了「开闭原则」
开闭原则:开放拓展,关闭修改!!
这里还有一个原则,把变化的部分抽离出来,使变化的部分独立客户端的使用
在这个例子里面,变化的部分就是每次需要根据不同类型制作不同的 Pizza,而不变的部分就是制作完 Pizza 后的四个步骤「准备、烘焙、切、装盒」
所以我们可以把「制作不同类型 Pizza」的部分抽离出来,放到工厂中,每次需要制作 Pizza 时,去工厂里取即可!
下面先给出对应的类图:
接着给出核心代码:
x// 简单工厂类public class SimplePizzaFactory { public Pizza createPizza(String type) { Pizza pizza = null; if (type.equals("cheese")) { pizza = new CheesePizza(); } else if (type.equals("greek")) { pizza = new GreekPizza(); } else if (type.equals("pepperoni")) { pizza = new Pepperoni(); } return pizza; }}
public class PizzaStore { SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) { this.factory = factory; }
public Pizza orderPizza(String type) { // 从工厂中获得 Pizza pizza = factory.createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; }}这样当需要添加新的 Pizza 类型或者删去已有的 Pizza 类型,只用在createPizza()中 修改即可!!
可能有人会提问:这不就是把orderPizza()中的判断逻辑移到了createPizza()中吗??这样不就只是把问题移到了另一个对象中吗??
确实只是把判断逻辑移到了createPizza()中,这样关闭了对orderPizza()的修改
在现实中不仅仅只有orderPizza()需要根据类型生成 Pizza,在PizzaShopClass中也需要这样的功能,PizzaShopClass也可以直接从工厂中获取 Pizza,而不需要在自己内部实现逻辑判断
如果不引入「简单工厂模式」,对于一个改变,可能会修改多处的代码,例如:orderPizza()的逻辑判断部分、PizzaShopClass中的逻辑判断部分等等
而引入「简单工厂模式」后,对于一个改变,只需要修改工厂中对应的逻辑判读部分即可!!
现在,我们在不同地区开了分店,为了迎合当地人的习惯,同一款在不同地区的口味也应该有所区别;也需要对『前期准备「Prepare」、烘焙「Bake」、切「Cut」、装盒「Box」』四个步骤进行有针对性的改进,即对于不同地区,相同的步骤需要改变
下面先给出对应的类图:
接着给出代码:
xxxxxxxxxx// Pizza 抽象类public abstract class Pizza { public String name; // 面团 public String dough; // 酱 public String sauce; // 浇头 public List<String> toppings = new ArrayList<>();
public void prepare() { System.out.println("准备 " + name); System.out.println("揉面团 ..."); System.out.println("添加酱汁 ..."); System.out.println("添加浇头:"); for (String topping : toppings) { System.out.println(" " + topping); } }
public void bake() { System.out.println("烘焙 25 分钟"); }
public void cut() { System.out.println("把 pizza 切成三角形切片"); }
public void box() { System.out.println("用官方的盒子包装"); }
public String getName() { return name; }}// 实现抽象类 Pizza (其他同理)public class NYStyleCheesePizza extends Pizza { // 根据不同地区不同风格,可以用不同的配料和制作细节 public NYStyleCheesePizza() { name = "NY Style Sauce and Cheese Pizza"; dough = "Thin Crust Dough"; sauce = "Marinara Sauce"; toppings.add("Grated Reggiano Cheese"); }}// PizzaStore 抽象类public abstract class PizzaStore { public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } // 抽象方法 protected abstract Pizza createPizza(String type);}// 实现抽象类 PizzaStore (其他同理)public class NYStylePizzaStore extends PizzaStore { protected Pizza createPizza(String item) { if (item.equals("cheese")) { return new NYStyleCheesePizza(); } else if (item.equals("veggie")) { return new NYStyleVeggiePizza(); } else if (item.equals("clam")) { return new NYStyleClamPizza(); } else if (item.equals("pepperoni")) { return new NYStylePepperoniPizza(); } else return null; }}由此可以引出抽象方法模式的定义:
The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
解释:工厂方法模式定义了一个创建对象的接口 (在该例子中为抽象类Pizza),让子类决定实例化哪个类 (在该例子中为NYStylePizzaStore)。工厂方法让类的实例化延迟到了子类中
为了严谨,这里再给出「工厂方法模式」的通用类图:
Depend upon abstractions. Do not depend upon concrete classes.
解释:依赖抽象,不要依赖具体的类
当我们未使用任何设计模式时,PizzaStore直接依赖具体的实现类,其依赖关系如下图所示:
当我们使用了「工厂方法模式」时,PizzaStore依赖抽象类Pizza;同时具体的 Pizza 也依赖抽象类Pizza,其依赖关系如下图所示:
我们的设计尽可能的遵循「依赖倒置」原则,这样可以使系统实现低耦合!!
先看工厂方法模式中的NYStyleCheesePizza类,dough、sauce、toppings等配料都是经销商自己制作,为了节约成本,存在偷工减料的情况,严重影响品牌口碑
为了杜绝这种情况,需要把不同地区不同类型 Pizza 的配料掌握在总部手中,即由总部分发配料到经销商手中,这样可以实现配料统一
下面先给出对应的类图:
接着给出核心代码:
xxxxxxxxxx// PizzaStore 同上public class NYStylePizzaStore 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; }}// 其他 (Sauce Cheese Clams) 同理public interface Dough { String toString();}// 其他同理public class ThickCrustDough implements Dough { public String toString() { return "Thick Crust Dough"; }}// 配料抽象工厂public interface PizzaIngredientFactory { Dough createDough(); Sauce createSauce(); Cheese createCheese(); Veggies[] createVeggies(); Pepperoni createPepperoni(); Clams createClams();}// ChicagoPizzaIngredientFactory 同理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() { return new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() }; } public Pepperoni createPepperoni() { return new SlicedPepperoni(); } public Clams createClams() { return new FreshClams(); }}// 抽象类 Pizzapublic abstract class Pizza { public String name; public Dough dough; public Sauce sauce; public Veggies veggies[]; public Cheese cheese; public Pepperoni pepperoni; public Clams clam;
protected Pizza() { }
public 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"); }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public String toString() { // code to print pizza here }}为了严谨,这里再给出「工厂方法模式」的通用类图:
由此可以引出抽象方法模式的定义:
The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
解释:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类
下面来梳理一下这两个模式的区别!!
核心不同点:「工厂方法模式」使用类创建对象;而「抽象工厂模式」使用对象创建对象
工厂方法模式通过继承的方式创建不同类型的对象。子类考虑具体的类型 (把类的实例化延迟到了子类中),而客户端只需要知道抽象的类型即可。且对于每一个子类,只能创建一种类型的对象,详情可见NYStylePizzaStore;而抽象工厂模式通过组合的方式创建一系列的对象
用大白话再解释一波:
工厂方法模式侧重点在「方法」,定义一个抽象方法返回一个抽象类型「protected abstract Pizza createPizza(String type);」,让子类去实现这个抽象方法,确定返回具体的对象类型,所以一个方法,只能创建一个对象
抽象工厂模式侧重点在「抽象工厂」,定义了一个工厂的接口,接口中有多个创建对象的方法,所以一个工厂,可以创建一系列的对象
抽象工厂的接口是由多个工厂方法组成!!