关闭

老僧长谈设计模式-2-工厂模式

305人阅读 评论(0) 收藏 举报
分类:

声明:本节内容主要来自Head First 一书

【概述】

工厂方法模式(Factory Method  Pattern)通过让子类决定创建的对象是什么,来达到将对象创建的过程封装的目的。

【目的】

使用工厂模式,会给你系统带来更大的可扩展性和尽量少的修改量。

符合 开-闭 设计原则。

【讲故事】

复杂的逻辑要用简单的故事讲清楚,是一个聪明的选择。

假设你有一个匹萨店

public  class PizzaStore
    {
       public Pizza orderPizza(string type) 
       {
           Pizza pizza;

           //根据Pizza类型,我们实例化正确的具体类。
           //这里的任何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 的一些制作流程
           pizza.prepare();
           pizza.bake();
           pizza.cut();
           pizza.box();
           return pizza;

       }
    }

但是你需要更多匹萨类型。。。。。

这样就存在一个问题:如果市场需求变化(出现新的风味的Pizza需求),那我们就要对这段代码一改再改。
违反了我们的设计原则:类应该对扩展开发,对修改关闭。
那怎么办?


封装创建对象的代码

把创建匹萨打代码移到另一个对象中由新对象专职创建匹萨,称这个新对象为 工厂。


建立一个简单的匹萨工厂

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 PepperoniPizza();
           }
           return pizza;
       }
    }

问:这么做有什么好处?似乎只是把问题搬到另一个对象罢了,问题依然存在。

答:别忘了,SimplePizzaFactory可以有许多的客户,虽然目前只看到orderPizza()方法是它的客户,
然而,可能还有PizzaShopMenu(比萨店菜单)类,会利用这个工厂来取得比萨的价钱和描述。

可能还有一个HomeDelivery(宅急送)类,会以与PizzaShop类不同的方法来处理匹萨。
所以,把创建比萨的代码包装进一个类,当以后实现改变时,只需要修改这个类即可。


经过不断的改造,你的匹萨店经营有成,大家都想做你的加盟店。那么问题来了,每家加盟店都可能想要提供不同风味的匹萨(纽约、芝加哥、加州),

你希望加盟店都能利用你的代码,好让匹萨的流程能保持不变。

你想让加盟店采用你的工程创建匹萨,在烘烤和切片的部分流程也要和你保持一致,那么你需要一个框架把加盟店和创建匹萨捆绑在一起的同时又保持一定的弹性。

创建者类

public abstract class PizzaStore{
	public Pizza orderPizza(String type){
		Pizza pizza;
		pizza = createPizza(type);
		
		pizza.prepare();
		pizza.bake();
		pizza.cute();
		pizza.box();
		
		return pizza;
	}
	
	//工厂方法
	protected abstract Pizza createPizza(String type);
	
	//其他的方法
	....
}

这样每个加盟店就可以各自决定如何制造匹萨啦,这样就保持了弹性。

是时候在纽约开一家匹萨店了:

public class NYPizzaStore extends PizzaStore {

    /**
     * 
     * @param item
     * @return 返回一个Pizza对象
     * 由子类全权负责该实例化哪一个具体Pizza
     */
	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;
    }
}


刚刚忽略了一个类,匹萨类本身

public abstract class Pizza {  
    protected String name;        //名称  
    protected String dough;       //面团  
    protected String sause;       //酱料  
    protected List<String> toppings = new ArrayList<String>();       //佐料  
      
      
    public void prepare() {  
        System.out.println("Preparing "+name);  
        System.out.println("Tossing dough");  
        System.out.println("Adding sause");  
        System.out.println("Adding toppings");  
        for(int i = 0;i < toppings.size();i++){  
            System.out.println("   "+toppings.get(i));  
        }  
    }  
  
    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 String getName(){  
        return name;  
    }  
}  


纽约匹萨

public class NYStyleCheesePizza extends Pizza{  
    public NYStyleCheesePizza(){  
        name = "Ny Style Sauce and Cheese Pizza";  
        dough = "Thin Crust Dough";  
        sause = "Marinara Sauce";  
          
        toppings.add("Crated Reggiano Cheese");  
    }  
  
}

芝加哥匹萨

public class ChicagoStyleCheesePizza extends Pizza {  
    public ChicagoStyleCheesePizza(){  
        name = "Chicago Style Deep Dish Cheese Pizza";  
        dough = "Extra Thick Crust Dough";  
        sause = "Plum Tomato Sauce";  
          
        toppings.add("Shredded Mozzarella Cheese");  
    }  
      
    public void cut(){  
        System.out.println("Cutting the Pizza into square slices");  
    }  
}  


你已等的太久,匹萨开卖了

public class PizzaTestDrive {  
    public static void main(String[] args) {  
        System.out.println("---------Joel 需要的芝加哥的深盘披萨---------");  
        ChicagoPizzaStore chicagoPizzaStore = new ChicagoPizzaStore();       //建立芝加哥的披萨店  
        Pizza joelPizza =chicagoPizzaStore.orderPizza("cheese");             //下订单  
        System.out.println("Joel ordered a " + joelPizza.getName() + "\n");  
          
        System.out.println("---------Ethan 需要的纽约风味的披萨---------");  
        NYPizzaStore nyPizzaStore = new NYPizzaStore();  
        Pizza ethanPizza = nyPizzaStore.orderPizza("cheese");  
        System.out.println("Ethan ordered a " + ethanPizza.getName() + "\n");  
          
    }  
}

认识工厂模式的时刻终于到了

【类图】

简单工厂模式:


工厂模式:




匹萨店图解析

纽约匹萨:薄饼,美味的酱料和少量的芝士

芝加哥匹萨:厚饼,重味的酱料和大量的芝士




工厂模式的精髓:

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

工厂方法的优势:

1 解决了对修改关闭,对扩展开放的问题。

2 将创建对象的代码集中在一个对象或方法中,可以避免代码的重复,方便以后的维护

3 客户在实例化对象时,只会依赖于接口,而不是具体类。针对接口编程,而不是针对实现编程。


编程建议:

  • 不要让类派生自具体类

结束语:

谢谢观看。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:213355次
    • 积分:2793
    • 等级:
    • 排名:第12830名
    • 原创:31篇
    • 转载:181篇
    • 译文:0篇
    • 评论:40条
    文章分类
    最新评论