【head first 设计模式学习笔记】工厂模式
2017-11-19 14:04
489 查看
简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。
简单工厂
简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。
考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把orderPizza中的关于制作pizza的部分单独提取到一个类中。
以下是 SimplePizzaFactory
PizzaStore是SimplePizzaFactory的客户,通过SimplePizzaFactory来取得pizza的实例。
一个PizzaStore在制作pizza时等于有了一个供应商一样的东西专门去制作,而PizzaStore不必要去关心这些的细节。这本来是可以在orderPizza中实现的,现在单独形成类时为了可以服务更多的商店(比如同时经营的连锁店)或者不同的吃法(比如送货上门)。这样的实现称为简单工厂模式,也可以使用静态方法来定义这个工厂方法,当然这样的实现有个缺点就是无法通过继承改变创建方法的行为。另外简单工厂不是一个设计模式,而只是一种编程习惯。
工厂方法
考虑更多加盟店的需求:他们需要他们自己风格的口味,但是加盟店的一些业务流程又必须严格按照总店进行处理。
我们要把createPizza放回到PizzaStore中,将其作为抽象方法,现在的PizzaStore就是各个加盟店的框架
ChicagoPizzaStore能根据客户需求来生产pizza,ChicagoPizzaStore是PizzaStore的子类,现在我们就是由子类来决定如何制作pizza的。
ChicagoStyleCheesePizza、ChicagoStyleVeggiePizza等Pizza类不一一在这里列出。
顺便贴一下Pizza类的代码
现在我们来订一份ChicagoStyleCheesePizza
通过工厂方法,我们实现了让各个不同地区的pizza店生产自己特定风格的pizza。如果还有其他加盟店,那么我们可以让新的类继承PizzaStore,这个子类提供createPizza方法来制作他们自己风格的pizza即可。
工厂方法模式让子类来决定需要创建的对象的是什么,就如这里我们让PizzaStore的子类创建自己风格的pizza。我们这里所说的“决定”,并非允许子类本身在运行时做决定,而是说在编写创建类(我们这里的PizzaStore)时,不需要知道实际创建的产品(Pizza)是什么,如果我们选择PizzaStore的子类ChicagoPizzaStore,将要生产的产品就被决定了。
在代码中使用new创建其他的类就是对其他类的依赖,减少对于具体类的依赖是件好事情,这就引出我们第六个设计原则:要依赖抽象,不要依赖具体类——这有点类似于我们第一个原则,但是这里更强调不能让高层组件依赖低层组件(PizzaStore是个高层组件,他的行为由Pizza来决定,PizzaStore创建所有不同的Pizza对象,而准备、烘焙等由pizza本身来完成),而且,两者都应该依赖于抽象。在这个例子中,XXXPizza
--> Pizza <-- PizzaStore ,而Pizza则是一个抽象类。
在设计系统时,先从底部向上抽象(抽象出Pizza类),然后撇开具体的类,在抽象层面拓展(设计PizzaStore),有几个原则可以帮助我们规范使用这个原则,这些并不是都要做到,只要尽力即可:
变量不可以持有具体类的引用(如果使用new就会持有具体类的引用,可以使用工厂模式来避免)。
不要让类派生自具体类(都是从抽象类或接口派生出来的)。
不要覆盖基类中已经实现的方法。(这样会导致继承的复杂度上升,同时说明基类不合适被继承——基类中已实现的方法该由子类共享)
抽象工厂模式:
现在我们回到pizza本身,对pizza不同地区的加盟店其使用的原料可能不同,比如不同的酱料、不同的面团、不同的海鲜佐料等,这时我们需要一个
生产原料的工厂,即抽象工厂类PizzaIngredientFactory来生产原料
纽约的pizza原料厂如下
既然pizza原料变了,自然就pizza的准备prepare方法就变了,pizza类的prepare方法应该被抽象。
PizzaStore类如下
具体的生产Pizza方法createPizza由PizzaStore的子类实现。
是时候来份定制Pizza了
抽象工厂是应对产品族概念的。对每个不同地区的PizzaStore来说,他们使用的原料不同,我们用原料工厂PizzaIngredientFactory
三者的不同点:1、简单工厂简单工厂方法中,包括一个“抽象产品类”(该类可以是接口Interface,也可以是实际的类Class),所有需要的产品类都是该“抽象产品类”的子类(如果是接口的话,那么就是说所有产品类都继承了该接口)。简单工厂一般只包含一个具体的工厂类,由该工厂类生成所有的产品类的对象。生成产品类的方法,其内部一般是类似于switch的结构,根据输入的标志,选择创建不同类型的对象。由于不知道创建的对象到底是哪个类的,所以方法的返回值的类型是“抽象产品类”。 2、工厂方法抽象工厂中,包括“抽象工厂类”和“抽象产品类”,同时包含不只一个工厂类。所有的工厂类都必须是“抽象工厂类”的子类,所有的产品都必须是“抽象产品类”的子类。和简单工厂比起来,工厂方法一般是从抽象工厂开始的。一般都是在抽象工厂类中提供一个静态方法,由该方法根据输入的标志,生成不同的具体工厂类,然后由具体的产品类生成具体的产品。注意,一个具体工厂类只能生成一种具体的产品类的对象,不同的具体工厂生成不同的产品,而不是像简单工厂中那样,一个工厂类可以生成多种不同产品类的对象。可以这么理解,在选择不同的具体工厂类的时候,就选择了生成的产品,相对于简单工厂,相当于将选择产品的动作提前了。因为不知道创建的具体工厂类到底是哪一个,所以生成具体工厂类的静态方法的返回值的类型是“抽象工厂类”。具体工厂类生成产品类的方法,返回值的类型也要求是“抽象产品类”(因为前端调用的时候,需要使用同样的代码来访问)。
3、抽象工厂抽象工厂和工厂方法很类似,区别如下: 工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
参考:
简单工厂
简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。
考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把orderPizza中的关于制作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由工厂类来完成 pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
以下是 SimplePizzaFactory
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; } }
PizzaStore是SimplePizzaFactory的客户,通过SimplePizzaFactory来取得pizza的实例。
一个PizzaStore在制作pizza时等于有了一个供应商一样的东西专门去制作,而PizzaStore不必要去关心这些的细节。这本来是可以在orderPizza中实现的,现在单独形成类时为了可以服务更多的商店(比如同时经营的连锁店)或者不同的吃法(比如送货上门)。这样的实现称为简单工厂模式,也可以使用静态方法来定义这个工厂方法,当然这样的实现有个缺点就是无法通过继承改变创建方法的行为。另外简单工厂不是一个设计模式,而只是一种编程习惯。
工厂方法
考虑更多加盟店的需求:他们需要他们自己风格的口味,但是加盟店的一些业务流程又必须严格按照总店进行处理。
我们要把createPizza放回到PizzaStore中,将其作为抽象方法,现在的PizzaStore就是各个加盟店的框架
public abstract class PizzaStore { abstract Pizza createPizza(String item); //抽象方法,这个的具体实现由各个加盟店决定 public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.getName() + " ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
ChicagoPizzaStore能根据客户需求来生产pizza,ChicagoPizzaStore是PizzaStore的子类,现在我们就是由子类来决定如何制作pizza的。
public class ChicagoPizzaStore extends PizzaStore { Pizza createPizza(String item) { if (item.equals("cheese")) { return new ChicagoStyleCheesePizza(); } else if (item.equals("veggie")) { return new ChicagoStyleVeggiePizza(); } else if (item.equals("clam")) { return new ChicagoStyleClamPizza(); } else if (item.equals("pepperoni")) { return new ChicagoStylePepperoniPizza(); } else return null; } }
ChicagoStyleCheesePizza、ChicagoStyleVeggiePizza等Pizza类不一一在这里列出。
public class ChicagoStyleCheesePizza extends Pizza { public ChicagoStyleCheesePizza() { name = "Chicago Style Deep Dish Cheese Pizza"; dough = "Extra Thick Crust Dough"; sauce = "Plum Tomato Sauce"; toppings.add("Shredded Mozzarella Cheese"); } void cut() { System.out.println("Cutting the pizza into square slices"); } }
顺便贴一下Pizza类的代码
public abstract class Pizza { String name; String dough; String sauce; ArrayList<String> toppings = new ArrayList<String>(); void prepare() { System.out.println("Prepare " + name); System.out.println("Tossing dough..."); System.out.println("Adding sauce..."); System.out.println("Adding toppings: "); for (String topping : toppings) { System.out.println(" " + topping); } } void bake() { System.out.println("Bake for 25 minutes at 350"); } void cut() { System.out.println("Cut the pizza into diagonal slices"); } void box() { System.out.println("Place pizza in official PizzaStore box"); } public String getName() { return name; } public String toString() { StringBuffer display = new StringBuffer(); display.append("---- " + name + " ----\n"); display.append(dough + "\n"); display.append(sauce + "\n"); for (String topping : toppings) { display.append(topping + "\n"); } return display.toString(); } }
现在我们来订一份ChicagoStyleCheesePizza
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore chicagoStore = new ChicagoPizzaStore(); Pizza pizza = chicagoStore.orderPizza("cheese"); System.out.println("Joel ordered a " + pizza.getName() + "\n"); } }
通过工厂方法,我们实现了让各个不同地区的pizza店生产自己特定风格的pizza。如果还有其他加盟店,那么我们可以让新的类继承PizzaStore,这个子类提供createPizza方法来制作他们自己风格的pizza即可。
工厂方法模式让子类来决定需要创建的对象的是什么,就如这里我们让PizzaStore的子类创建自己风格的pizza。我们这里所说的“决定”,并非允许子类本身在运行时做决定,而是说在编写创建类(我们这里的PizzaStore)时,不需要知道实际创建的产品(Pizza)是什么,如果我们选择PizzaStore的子类ChicagoPizzaStore,将要生产的产品就被决定了。
在代码中使用new创建其他的类就是对其他类的依赖,减少对于具体类的依赖是件好事情,这就引出我们第六个设计原则:要依赖抽象,不要依赖具体类——这有点类似于我们第一个原则,但是这里更强调不能让高层组件依赖低层组件(PizzaStore是个高层组件,他的行为由Pizza来决定,PizzaStore创建所有不同的Pizza对象,而准备、烘焙等由pizza本身来完成),而且,两者都应该依赖于抽象。在这个例子中,XXXPizza
--> Pizza <-- PizzaStore ,而Pizza则是一个抽象类。
在设计系统时,先从底部向上抽象(抽象出Pizza类),然后撇开具体的类,在抽象层面拓展(设计PizzaStore),有几个原则可以帮助我们规范使用这个原则,这些并不是都要做到,只要尽力即可:
变量不可以持有具体类的引用(如果使用new就会持有具体类的引用,可以使用工厂模式来避免)。
不要让类派生自具体类(都是从抽象类或接口派生出来的)。
不要覆盖基类中已经实现的方法。(这样会导致继承的复杂度上升,同时说明基类不合适被继承——基类中已实现的方法该由子类共享)
抽象工厂模式:
现在我们回到pizza本身,对pizza不同地区的加盟店其使用的原料可能不同,比如不同的酱料、不同的面团、不同的海鲜佐料等,这时我们需要一个
生产原料的工厂,即抽象工厂类PizzaIngredientFactory来生产原料
public interface PizzaIngredientFactory { public Dough createDough(); public Sauce createSauce(); public Cheese createCheese(); public Veggies[] createVeggies(); public Pepperoni createPepperoni(); public Clams createClam(); }
纽约的pizza原料厂如下
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(); } }
既然pizza原料变了,自然就pizza的准备prepare方法就变了,pizza类的prepare方法应该被抽象。
public abstract class Pizza { String name; Dough dough; Sauce sauce; Veggies veggies[]; Cheese cheese; Pepperoni pepperoni; Clams clam; abstract void prepare(); //prepare方法为抽象方法,具体由pizza的子类实现 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() { StringBuffer result = new StringBuffer(); result.append("---- " + name + " ----\n"); if (dough != null) { result.append(dough); result.append("\n"); } if (sauce != null) { result.append(sauce); result.append("\n"); } if (cheese != null) { result.append(cheese); result.append("\n"); } if (veggies != null) { for (int i = 0; i < veggies.length; i++) { result.append(veggies[i]); if (i < veggies.length-1) { result.append(", "); } } result.append("\n"); } if (clam != null) { result.append(clam); result.append("\n"); } if (pepperoni != null) { result.append(pepperoni); result.append("\n"); } return result.toString(); } }
列出一个Pizza的实现类如下:
public class ClamPizza extends Pizza { PizzaIngredientFactory ingredientFactory; public ClamPizza(PizzaIngredientFactory ingredientFactory) { this.ingredientFactory = ingredientFactory; } void prepare() { System.out.println("Preparing " + name); dough = ingredientFactory.createDough(); sauce = ingredientFactory.createSauce(); cheese = ingredientFactory.createCheese(); clam = ingredientFactory.createClam(); } }
PizzaStore类如下
public abstract class PizzaStore { protected abstract Pizza createPizza(String item); public Pizza orderPizza(String type) { Pizza pizza = createPizza(type); System.out.println("--- Making a " + pizza.g c2cc etName() + " ---"); pizza.prepare(); pizza.bake(); pizza.cut(); pizza.box(); return pizza; } }
具体的生产Pizza方法createPizza由PizzaStore的子类实现。
比如NYPizzaStore
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; } }
是时候来份定制Pizza了
public class PizzaTestDrive { public static void main(String[] args) { PizzaStore nyStore = new NYPizzaStore(); Pizza pizza = nyStore.orderPizza("cheese"); System.out.println("Ethan ordered a " + pizza + "\n"); } }
抽象工厂是应对产品族概念的。对每个不同地区的PizzaStore来说,他们使用的原料不同,我们用原料工厂PizzaIngredientFactory
的实现类(不同的工厂原料不同)来给特定地区的pizza店提供原料。
抽象工厂方法提供了一个接口(此例中为PizzaIngredientFactory),用于创建相关或者依赖的对象家族,而不需要明确指定具体类,每个家族成员(此例为NYPizzaIngredientFactory)都负责创建一个具体的产品。与工厂方法的区别在于:工厂方法使用继承,针对的是类,利用工厂方法创建对象要扩展一个类,并覆盖它的工厂方法(例如这里的createPizza)——用来创建对象,工厂方法的实质在于通过子类创建对象;抽象工厂方法使用的是组合(ingredientFactory),针对的是一族对象,要使用这个工厂要首先将其实例化,然后将它传入一些针对抽象类型所写的代码中,优点在于可以把一群相关的产品集合起来,具体的工厂都是由工厂方法(prepare)创建的(这就是工厂方法和抽象工厂方法的联系)。
三者的不同点:1、简单工厂简单工厂方法中,包括一个“抽象产品类”(该类可以是接口Interface,也可以是实际的类Class),所有需要的产品类都是该“抽象产品类”的子类(如果是接口的话,那么就是说所有产品类都继承了该接口)。简单工厂一般只包含一个具体的工厂类,由该工厂类生成所有的产品类的对象。生成产品类的方法,其内部一般是类似于switch的结构,根据输入的标志,选择创建不同类型的对象。由于不知道创建的对象到底是哪个类的,所以方法的返回值的类型是“抽象产品类”。 2、工厂方法抽象工厂中,包括“抽象工厂类”和“抽象产品类”,同时包含不只一个工厂类。所有的工厂类都必须是“抽象工厂类”的子类,所有的产品都必须是“抽象产品类”的子类。和简单工厂比起来,工厂方法一般是从抽象工厂开始的。一般都是在抽象工厂类中提供一个静态方法,由该方法根据输入的标志,生成不同的具体工厂类,然后由具体的产品类生成具体的产品。注意,一个具体工厂类只能生成一种具体的产品类的对象,不同的具体工厂生成不同的产品,而不是像简单工厂中那样,一个工厂类可以生成多种不同产品类的对象。可以这么理解,在选择不同的具体工厂类的时候,就选择了生成的产品,相对于简单工厂,相当于将选择产品的动作提前了。因为不知道创建的具体工厂类到底是哪一个,所以生成具体工厂类的静态方法的返回值的类型是“抽象工厂类”。具体工厂类生成产品类的方法,返回值的类型也要求是“抽象产品类”(因为前端调用的时候,需要使用同样的代码来访问)。
3、抽象工厂抽象工厂和工厂方法很类似,区别如下: 工厂方法模式:
一个抽象产品类,可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。
一个抽象工厂类,可以派生出多个具体工厂类。
每个具体工厂类可以创建多个具体产品类的实例。
参考:
http://www.cnblogs.com/gnuhpc/archive/2012/12/17/2822403.htmlhttp://zyjustin9.iteye.com/blog/2094960
相关文章推荐
- 《Head First 设计模式》学习笔记:工厂模式
- Head First 设计模式学习笔记(4)---工厂模式
- C++设计模式学习笔记五:工厂方法模式
- 设计模式学习笔记(四):简单工厂模式
- 《Head First 设计模式》学习笔记:策略模式与观察者模式
- 《Head First 设计模式》学习笔记:装饰者模式
- 设计原则(head first 设计模式)学习笔记
- 学习笔记--设计模式之工厂模式
- 设计模式学习笔记(四)之工厂模式(Factory)
- 学习head first 设计模式之工厂模式
- (Head First 设计模式)学习笔记(1)
- 《Head First 设计模式》学习笔记一:设计模式入门
- 学习设计模式笔记--简单工厂
- .Net学习笔记----2015-06-27(简单工厂设计模式)
- 设计模式学习笔记之工厂模式
- 设计模式学习笔记(四)之工厂模式(Factory)
- 设计模式学习笔记---工厂模式(Java版)
- JAVA学习笔记-----设计模式之工厂模式
- java设计模式学习笔记之计算器中的简单工厂模式
- 设计模式学习笔记:factory method(工厂方法)