【head first 设计模式学习笔记】工厂模式

来源:互联网 发布:cms色彩管理系统 编辑:程序博客网 时间:2024/06/14 16:17

简单工厂,工厂方法,抽象工厂都属于设计模式中的创建型模式。其主要功能都是帮助我们把对象的实例化部分抽取了出来,优化了系统的架构,并且增强了系统的扩展性。


简单工厂

简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。


考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把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.getName() + " ---");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.html

http://zyjustin9.iteye.com/blog/2094960

阅读全文
0 0
原创粉丝点击