【设计模式】02-HeadFirst工厂模式

本文通过制作Pizza的例子详细解析了简单工厂模式与工厂方法模式。简单工厂模式将创建对象的逻辑封装在一个静态方法中,而工厂方法模式则通过定义一个创建对象的接口,允许子类决定实例化哪一个类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自 http://blog.youkuaiyun.com/u010800530/article/details/45801353

设计模式要求我们不应该针对实现编程,是为了降低耦合度,提高可维护性。当程序中出现“new”的时候,就证明程序在实例化一个具体类,所以用的是实现,而不是接口。如果代码绑着具体的类会导致代码更加脆弱,缺乏弹性。比如,需要创建一“个鸡蛋饼”这个对象,首先需要创建一个饼,然后创建一个鸡蛋,再然后把鸡蛋摊在饼上边,然后给饼翻翻,几分钟后就出炉了....(有点饿)。在这种情况下,新对象的建立是一个过程,如果我们需要在这个饼上边抹点辣椒酱,那肯定需要对类进行修改,违反了“对扩展开放,对修改关闭”的设计原则。这样就出现了一个问题:

     如何让客户直接构造出对象的实例,而不用在乎构造对象实例的具体细节?(就比如说,我想吃鸡蛋饼,直接就能买到,不需要知道这个鸡蛋饼如何做出来的)

具体实现:

首先,我们需要知道,工厂模式包含了三种:简单工厂(静态工厂)、工厂方法、抽象工厂。但是简单工厂并不属于GOF的23种设计模式

下边会用制作Pizza的例子来对三个模式进行说明:

假设你有一个Pizza店,并且你有很多种类型的Pizza,那么你会这样写代码:

[java]  view plain  copy
  1. Pizza orderPizza(String type){  
  2.         Pizza pizza;  
  3.           
  4.         if(type.equals("chesse")){  
  5.             pizza = new CheesePizza();  
  6.         }else if(type.equals("greek")){  
  7.             pizza = new GreekPizza();  
  8.         }else if(type.equals("pepperoni")){  
  9.             pizza = new PepperoniPizza();  
  10.         }  
  11.           
  12.         pizza.prepare();  
  13.         pizza.bake();  
  14.         pizza.cut();  
  15.         pizza.box();  
  16.         return pizza;  
  17.     }  
根据参数传入类型type的不同,实例化不同的Pizza。

但是,你的竞争对象已经在菜单中加入了其它流行风味Pizza:ClamPizza(蛤蜊披萨)、VeggiePizza(素食披萨)。如果你想要赶上他们你就必须在自己的菜单中加入这些风味的披萨,而GreekPizza(希腊披萨)因为卖的不好所以去掉:


很明显的,orderPizza()方法不能够使得对修改关闭,但是我们已经知道哪些会改变,哪些不会改变,就可以使用封装了。

一、简单工厂模式:

要把创建Pizza的代码移动到另一个对象中,由这个新对象专职创建Pizza:

[java]  view plain  copy
  1. public class SimplePizzaFactory {  
  2.     public Pizza createPizza(String type) {  
  3.         Pizza pizza = null;  
  4.   
  5.         if (type.equals("cheese")) {  
  6.             pizza = new CheesePizza();  
  7.         } else if (type.equals("pepperoni")) {  
  8.             pizza = new PepperoniPizza();  
  9.         } else if (type.equals("clam")) {  
  10.             pizza = new ClamPizza();  
  11.         } else if (type.equals("veggie")) {  
  12.             pizza = new VeggiePizza();  
  13.         }  
  14.         return pizza;  
  15.     }  
  16. }  
问题:这么做似乎是把问题搬到另一个对象罢了?似乎没有什么好处?

答:SimplePizzaFactory可以有许多的客户,虽然目前仅仅只有orderPizza()方法是它的客户,但是在以后的扩展过程中可能会有很多个客户(但是如果把createPizza()这个方法放入到orderPizza()方法中的话,它只可能为orderPizza()一个服务)。所以,把创建Pizza的代码包装进一个类后,当以后实现改变的话,只需要修改这个类即可。我们正需要做的就是把实例化的过程,从客户的代码中删除。

缺点:因为是静态的,不能够通过继承来改变创建方法的行为。

下边,我们把其它的类写出来:

PizzaStore:是SimplePizzaFactory工厂类的“客户”,PizzaStor通过工厂类取得Pizza的实例

[java]  view plain  copy
  1. public class PizzaStore {  
  2.     SimplePizzaFactory factory;  
  3.   
  4.     public PizzaStore(SimplePizzaFactory factory) {  
  5.         this.factory = factory;  
  6.     }  
  7.   
  8.     public Pizza orderPizza(String type) {  
  9.         Pizza pizza;  
  10.   
  11.         pizza = factory.createPizza(type);  
  12.   
  13.         pizza.prepare();  
  14.         pizza.bake();  
  15.         pizza.cut();  
  16.         pizza.box();  
  17.   
  18.         return pizza;  
  19.     }  
  20. }  
Pizza:工厂的产品

[java]  view plain  copy
  1. abstract public class Pizza {  
  2.     String name;  
  3.     String dough;  
  4.     String sauce;  
  5.     ArrayList<String> toppings = new ArrayList<String>();  
  6.   
  7.     public String getName() {  
  8.         return name;  
  9.     }  
  10.   
  11.     public void prepare() {  
  12.         System.out.println("Preparing " + name);  
  13.     }  
  14.   
  15.     public void bake() {  
  16.         System.out.println("Baking " + name);  
  17.     }  
  18.   
  19.     public void cut() {  
  20.         System.out.println("Cutting " + name);  
  21.     }  
  22.   
  23.     public void box() {  
  24.         System.out.println("Boxing " + name);  
  25.     }  
  26.   
  27.     public String toString() {  
  28.         StringBuffer display = new StringBuffer();  
  29.         display.append("---- " + name + " ----\n");  
  30.         display.append(dough + "\n");  
  31.         display.append(sauce + "\n");  
  32.         for (int i = 0; i < toppings.size(); i++) {  
  33.             display.append(toppings.get(i) + "\n");  
  34.         }  
  35.         return display.toString();  
  36.     }  
  37. }  
CheesePizza:

[java]  view plain  copy
  1. public class CheesePizza extends Pizza {  
  2.     public CheesePizza() {  
  3.         name = "Cheese Pizza";  
  4.         dough = "Regular Crust";  
  5.         sauce = "Marinara Pizza Sauce";  
  6.         toppings.add("Fresh Mozzarella");  
  7.         toppings.add("Parmesan");  
  8.     }  
  9. }  
VeggiePizza:

[java]  view plain  copy
  1. public class VeggiePizza extends Pizza {  
  2.     public VeggiePizza() {  
  3.         name = "Veggie Pizza";  
  4.         dough = "Crust";  
  5.         sauce = "Marinara sauce";  
  6.         toppings.add("Shredded mozzarella");  
  7.         toppings.add("Grated parmesan");  
  8.         toppings.add("Diced onion");  
  9.         toppings.add("Sliced mushrooms");  
  10.         toppings.add("Sliced red pepper");  
  11.         toppings.add("Sliced black olives");  
  12.     }  
  13. }  
ClamPizza:

[java]  view plain  copy
  1. public class ClamPizza extends Pizza {  
  2.     public ClamPizza() {  
  3.         name = "Clam Pizza";  
  4.         dough = "Thin crust";  
  5.         sauce = "White garlic sauce";  
  6.         toppings.add("Clams");  
  7.         toppings.add("Grated parmesan cheese");  
  8.     }  
  9. }  

写一个Main类:PizzaTestDrive

[java]  view plain  copy
  1. public class PizzaTestDrive {  
  2.   
  3.     public static void main(String[] args) {  
  4.         SimplePizzaFactory factory = new SimplePizzaFactory();  
  5.         PizzaStore store = new PizzaStore(factory);  
  6.   
  7.         Pizza pizza = store.orderPizza("cheese");  
  8.         System.out.println("We ordered a " + pizza.getName() + "\n");  
  9.   
  10.         pizza = store.orderPizza("veggie");  
  11.         System.out.println("We ordered a " + pizza.getName() + "\n");  
  12.     }  
  13. }  

把UML类图画出来了:


二、工厂方法模式

特点:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类

现在,大家都希望能够在自家附近加盟你开的披萨店(其实就是想用你的招牌在他们那里开店啦)。但是,每个区域都会有差异,每家加盟店都想要提供不同风味的比萨(比方说纽约、芝加哥、加州),你想这样做:


[java]  view plain  copy
  1. //纽约风味的素食Pizza  
  2. NYPizzaFactory nyFactory = new NYPizzaFactory();  
  3. PizzaStore nyStore = new PizzaStore(nyFactory);  
  4. nyStore.orderPizza("Veggie");  
  5.   
  6. //芝加哥风味的素食Pizza  
  7. ChicagePizzaFactory chicagoFactory = new ChicagePizzaFactory();  
  8. PizzaStore chicagoStore = new PizzaStore(chicagoFactory);  
  9. chicagoStore.orderPizza("Veggie");  
问题:但是,在推广你的方法的时候,别的加盟店的确是采用你的工厂创建比萨,但是其他部分却开始此采用他们自创的流程:烘烤的做法有差异、不要切片、使用其他厂商的盒子等等。

有一种做法可以让比萨制作活动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由的制作本地区域的风味:

            把createPizza()方法放回到PizzaStore中,不过要把它设置成“抽象方法”:(原本是由一个对象负责所有具体类的实例化,现在通过对PizzaStor做一些转变,变成由一群子类来负责实例化)

声明一个工厂类:

[java]  view plain  copy
  1. public abstract class PizzaStore {  
  2.     //把createPizza()方法设置成抽象的,由子类做决定  
  3.     abstract Pizza createPizza(String item);  
  4.       
  5.     public Pizza orderPizza(String type) {  
  6.         Pizza pizza = createPizza(type);  
  7.         System.out.println("--- Making a " + pizza.getName() + " ---");  
  8.           
  9.         pizza.prepare();  
  10.         pizza.bake();  
  11.         pizza.cut();  
  12.         pizza.box();  
  13.         return pizza;  
  14.     }  
  15. }  
然后声明两个具体工厂类:NYPizzaStore、ChicagoPizzaStore

[java]  view plain  copy
  1. public class NYPizzaStore extends PizzaStore {  
  2.     Pizza createPizza(String item) {  
  3.         if (item.equals("cheese")) {  
  4.             return new NYStyleCheesePizza();  
  5.         } else if (item.equals("veggie")) {  
  6.             return new NYStyleVeggiePizza();  
  7.         } else if (item.equals("clam")) {  
  8.             return new NYStyleClamPizza();  
  9.         } else if (item.equals("pepperoni")) {  
  10.             return new NYStylePepperoniPizza();  
  11.         } else return null;  
  12.     }  
  13. }  

[java]  view plain  copy
  1. public class ChicagoPizzaStore extends PizzaStore {  
  2.     Pizza createPizza(String item) {  
  3.             if (item.equals("cheese")) {  
  4.                     return new ChicagoStyleCheesePizza();  
  5.             } else if (item.equals("veggie")) {  
  6.                     return new ChicagoStyleVeggiePizza();  
  7.             } else if (item.equals("clam")) {  
  8.                     return new ChicagoStyleClamPizza();  
  9.             } else if (item.equals("pepperoni")) {  
  10.                     return new ChicagoStylePepperoniPizza();  
  11.             } else return null;  
  12.     }  
  13. }  
我们需要建立一个Pizza实体类:

[java]  view plain  copy
  1. public abstract class Pizza {  
  2.     String name; //名称  
  3.     String dough; //面团类型  
  4.     String sauce; //酱料  
  5.     ArrayList<String> toppings = new ArrayList<String>(); //作料  
  6.   
  7.     void prepare() {  
  8.         System.out.println("准备 " + name);  
  9.         System.out.println("揉面团...");  
  10.         System.out.println("添加酱料...");  
  11.         System.out.println("添加作料: ");  
  12.         for (int i = 0; i < toppings.size(); i++) {  
  13.             System.out.println("   " + toppings.get(i));  
  14.         }  
  15.     }  
  16.     void bake() {  
  17.         System.out.println("烘烤25分钟");  
  18.     }  
  19.     void cut() {  
  20.         System.out.println("把Pizza对角切片");  
  21.     }  
  22.     void box() {  
  23.         System.out.println("把Pizza装盒子");  
  24.     }  
  25.     public String getName() {  
  26.         return name;  
  27.     }  
  28. }  
然后需要一些具体的子类,下边定义两个子类:纽约风味的芝士披萨(NYStyleCheesePizza)、芝加哥风味的芝士披萨(ChicageStyleCheesePizza)

[java]  view plain  copy
  1. public class NYStyleCheesePizza extends Pizza {  
  2.     public NYStyleCheesePizza() {   
  3.         name = "NY Style Sauce and Cheese Pizza";  
  4.         dough = "Thin Crust Dough";  
  5.         sauce = "Marinara Sauce";  
  6.    
  7.         toppings.add("Grated Reggiano Cheese");  
  8.     }  
  9. }  

[java]  view plain  copy
  1. public class ChicagoStyleCheesePizza extends Pizza {  
  2.     public ChicagoStyleCheesePizza() {   
  3.         name = "Chicago Style Deep Dish Cheese Pizza";  
  4.         dough = "Extra Thick Crust Dough";  
  5.         sauce = "Plum Tomato Sauce";  
  6.    
  7.         toppings.add("Shredded Mozzarella Cheese");  
  8.     }  
  9.     //可以覆盖cut()方法  
  10.     void cut() {  
  11.         System.out.println("Cutting the pizza into square slices");  
  12.     }  
  13. }  

我们把UML类图画出来:


如果我们需要一个纽约风味的芝士披萨,我们应该怎么做:

(1) 需要一个纽约比萨店:PizzaStore nyPizzaStore = new NYPizzaStore();

(2) 下订单:nyPizzaStore.orderPizza("cheese");

(3) orderPizza()方法调用createPizza()方法:Pizza pizza = createPizza("cheese");
(4) 最后经过:pizza.prepare()、pizza.bake()、pizza.cut()、pizza.box()才能完成Pizza


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值