场景 披萨店
简单工厂 场景描述 披萨店提供预定
功能,可以提供多种类型(参数type
)披萨的预定;已知的是:a)披萨店会时常调整所提供的披萨种类,b)披萨制作过程(包含:准备、烘焙、切片、包装)不会经常变化;代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class PizzaStore { 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 PepperoniPizza (); } pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
采用这样的设计,每当披萨店有新品披萨上架、或下架某类披萨时就需要对该代码进行修改,如下图所示:
解决方案 使用简单工厂,对orderPizza(String type)
方法中变化的部分进行封装,使orderPizza(String type)
方法对“修改关闭”,于是我们将创建对象的代码从orderPizza(String type)
方法中抽象出来,如下;这样orderPizza(String type)
方法只需要从工厂中得到比萨对象,然后对它进行一系列的制作过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class SimplePizzaFactory { public Pizza createPizza (String type) { Pizza pizza = null ; if (type.equals("cheese" )) { pizza = new CheesePizza(); } else if (type.equals("pepperoni" )) { pizza = new PepperoniPizza(); } else if (type.equals("clam" )) { pizza = new ClamPizza(); } else if (type.equals("veggie" )) { pizza = new VeggiePizza(); } return pizza; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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; } }
简单工厂的定义
工厂方法 工厂方法的定义
场景描述 现在考虑开展披萨加盟店,经营者为保障加盟店营运的质量,希望这些加盟店尽量使用之前披萨店的代码(即,PizzaStore
);又已知的是,每个地区的加盟店可能想要提供不同风味的比萨。按照简单工厂的方式,一个可能的设计可能会是:每家加盟店都有自己的工厂来创建具体的披萨对象,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class NYPizzaFactory { public Pizza createPizza (String type) { } } public class ChicagoPizzaFactory { public Pizza createPizza (String type) { } } public class CaliforniaPizzaFactory { public Pizza createPizza (String type) { } }
但是调研中发现,并不是所有的加盟者都希望完全按照PizzaStore
的流程来制作披萨,往往他们都是一些有着丰富经验的厨师,他们想要加入自己的一些过程设计。这样的话,PizzaStore
就不能满足需要了,因为它把制作披萨的过程牢牢地绑定到了其内部。
解决方案 我们a)把PizzaStore
类声明为抽象类,b)把简单工厂中的方法拿回到PizzaStore
类中,并将其声明为抽象方法,c)为每个区域的店创建一个PizzaStore
类的子类。这样,可以让比萨的制作活动仍然局限在PizzaStore
类,而同时又让这些加盟店能够自由地制作该区域风味的披萨。我们称createPizza()
为工厂方法,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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) ; }
纽约州的披萨店:
1 2 3 4 5 6 7 8 9 10 11 12 13 public class NYPizzaStore extends PizzaStore { 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 ; } }
如此一来,每个地区的加盟店类型都继承PizzaStore
类,并各自决定如何制作自己的披萨(即,定义各自的createPizza()
方法);并且每个PizzaStore
的具体子类都有自己的比萨变体。这样就做到了所有加盟店仍然使用PizzaStore框架,并可以使用已经非常不错的订单系统(即,orderPizza()
方法)。
抽象工厂 抽象工厂模式的定义 抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族 ,而不需要明确指定具体类。
每个具体工厂都可以生产一整组产品。
场景描述 为了确保加盟店使用高质量的原料,我们打算建造原料工厂,并将原料运送到各家加盟店;并且,针对不同区域的加盟店,原料工厂能提供只适合该区域加盟店的原料。
已知所有披萨所需要的原料种类都是相同的(面团、酱料、芝士、海鲜佐料),这些原料的制作方式根据区域的不同而有差异。
解决方案 工厂接口PizzaIngredientFactory
中定义所有原料创建的方法:
1 2 3 4 5 6 7 8 public interface PizzaIngredientFactory { public Dough createDough () ; public Sauce createSauce () ; public Cheese createCheese () ; public Veggies[] createVeggies(); public Pepperoni createPepperoni () ; public Clams createClam () ; }
为每个区域建造一个工厂,使用该区域所特有的一组原料供工厂使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare () ; void bake () { System.out.println("Bake for 25 minutes at 350" ); } void cut () { System.out.println("Cutting the pizza into diagonal slices" ); } 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 () { } }
奶酪披萨使用工厂接口PizzaIngredientFactory
来准备原料:
1 2 3 4 5 6 7 8 9 10 11 12 public class CheesePizza extends Pizza { PizzaIngredientFactory ingredientFactory; public CheesePizza (PizzaIngredientFactory ingredientFactory) { this .ingredientFactory = ingredientFactory; } void prepare () { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); } }
具体的披萨店知道从哪个工厂进行原料采购:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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; } }
工厂方法VS抽象工厂
工厂方法
抽象工厂
1.不需要另外定义接口,只需要定义抽象方法; 2.使用继承,把对象的创建委托给子类
1.抽象工厂的任务是定义一个负责创建一组产品的接口,接口中的每个方法都负责创建一个具体产品; 2.使用对象组合,对象的创建被实现在工厂接口所暴露出来的方法中;
参考
https://wickedlysmart.com/head-first-design-patterns/