`

转:【HeadFirst 设计模式学习笔记】4 工厂模式

 
阅读更多

原文出处:http://www.cnblogs.com/gnuhpc/archive/2012/12/17/2822403.html

 

1.我们在前边提到一个原则就是“面向接口而不要面向实现编程”,但是我们一直在使用Duck duck = new MallardDuck()这样的模式,后边的new语句却违反了这个原则。工厂方法就可以解决这个问题,它用来封装对象的创建。

2.这一节我们考虑如下场景:一个披萨店要制作各种各样的披萨,甚至还要开分店。其中涉及到订购披萨的步骤。我们可以把orderPizza中的关于制作pizza的部分单独提取到一个类中。

public class PizzaStore { 
    SimplePizzaFactory factory;//为pizza工厂设立一个引用,在后边制作pizza时就会用到。 
    public PizzaStore(SimplePizzaFactory factory) { //需要传入一个工厂作为参数。 
        this.factory = factory; 
    } 
    public Pizza orderPizza(String type) { 
        Pizza pizza;


        pizza = factory.createPizza(type);//这里不再使用new,而是通过工厂方法创建,从而到达了抽象这部分代码的作用

        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("pepperoni")) { 
            pizza = new PepperoniPizza(); 
        } else if (type.equals("clam")) { 
            pizza = new ClamPizza(); 
        } else if (type.equals("veggie")) { 
            pizza = new VeggiePizza(); 
        } 
        return pizza; 
    } 
}

这样,一个pizza商店在制作pizza时等于有了一个供应商一样的东西专门去制作,而商店不必要去关心这些的细节。这本来是可以再orderPizza中实现的,现在单独形成类时为了可以服务更多的商店(比如同时经营的连锁店)或者不同的吃法(比如送货上门)。这样的实现称为简单工厂模式,也可以使用静态方法来定义者这个工厂方法,当然这样的实现有个缺点就是无法通过继承改变创建方法的行为。注意,简单工厂模式并不等价于工厂模式。

 

3.工厂方法:现在我们考虑更多加盟店的需求:他们需要他们自己风格的口味,但是加盟店的一些业务流程又必须严格按照总店进行处理。

 

针对这样的需求,我们重新设计PizzaStore类,将createPizza方法从SimplePizzaFactory 中移出来,设置为抽象的——加盟店必须自己完成这个方法自定义他们的Pizza,而orderPizza就保证了流程都是一样的,要是担心新的加盟店在继承后通过覆盖而改动这个方法则可以在前边加上final:

 

public abstract class PizzaStore {//定义这个抽象类的意思是这个类必须通过继承才能创建对象,这个类被称作抽象工厂类,提供了一个抽象的工厂方法供子类实现这个方法。注意面向接口编程。 
    abstract Pizza createPizza(String item);//工厂方法,这里也可以不是抽象而定义一些基本操作,具体工厂类就可以不需要任何操作就可以创建一个默认的“披萨”。


    public Pizza orderPizza(String type) {       

        
Pizza pizza = createPizza(type);//注意在这里的createPizza方法只是一个抽象方法,具体实现由子类完成——这就是工厂方法所谓的一大特点的表现:“让类把实例化推迟到子类”——因为orderPizza方法并不知道哪个子类将实际上制作Pizza(这个方法只知道一个流程)——这是由具体的XXXPizzaStore来决定的。 

        System.out.println("--- Making a " + pizza.getName() + " ---"); 
        pizza.prepare(); 
        pizza.bake(); 
        pizza.cut(); 
        pizza.box(); 
        return pizza; 
    } 
}

 

此时,我们开一家芝加哥的分店:

public class ChicagoPizzaStore extends PizzaStore { //继承自总店的模式,有点像简单工厂方法中的工厂类,但是区别在于工厂方法创建了一个框架,让子类决定要如何实现。

    Pizza createPizza(String item) {//实现了createPizza这个方法,并且面向接口编程返回Pizza。当然,这个函数也可以不传参  
            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; 
    } 
}

 

我们然后实现pizza本身的一些事情:

public abstract class Pizza {//这个类被称为抽象产品类,也就是说工厂方法产生的产品都是这个类的子类。 
    String name; 
    String dough; 
    String sauce; 
    ArrayList toppings = new ArrayList();//这个ArrayList维护了一套佐料 
    void prepare() { 
        System.out.println("Preparing " + name); 
        System.out.println("Tossing dough..."); 
        System.out.println("Adding sauce..."); 
        System.out.println("Adding toppings: "); 
        for (int i = 0; i < toppings.size(); i++) { 
            System.out.println("   " + toppings.get(i)); 
        } 
    } 
    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"); 
    } 
    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 (int i = 0; i < toppings.size(); i++) { 
            display.append((String )toppings.get(i) + "/n"); 
        } 
        return display.toString(); 
    } 
}

 

根据总店的规定,我们来自定义一个芝加哥风味的pizza:

public class ChicagoStyleClamPizza extends Pizza 
    public ChicagoStyleClamPizza() { 
        name = "Chicago Style Clam Pizza"; 
        dough = "Extra Thick Crust Dough"; 
        sauce = "Plum Tomato Sauce"; 
        toppings.add("Shredded Mozzarella Cheese"); 
        toppings.add("Frozen Clams from Chesapeake Bay"); 
    } 
    void cut() { 
        System.out.println("Cutting the pizza into square slices"); 
    } 
}

 

OK,All done,我们可以去吃一顿披萨大餐了:

 

public class PizzaTestDrive { 
    public static void main(String[] args) { 
        PizzaStore chicagoStore = new ChicagoPizzaStore(); 
        Pizza pizza = chicagoStore.orderPizza("clam");//依赖于接口 
        } 
}

 

4.工厂方法模式的定义:定义了一个创建对象的接口(abstract createPizza),但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。优点在于将创建对象的代码集中在一个类或者方法中,可以避免代码中的重复,并且方便以后的管理,并且客户在实例化对象时只会依赖于接口而不是具体的类。在代码中使用new创建其他的类就是对其他类的依赖,减少对于具体类的依赖是件好事情,这就引出我们第六个设计原则:要依赖抽象,不要依赖具体类——这有点类似于我们第一个原则,但是这里更强调不能让高层组件依赖低层组件,而且,两者都应该依赖于抽象。在这个例子中,XXXPizza  --> Pizza <- PizzaStore ,而Pizza则是一个抽象类,这就是well designed.思维的方式是逆向思维,在设计系统时,先从底部向上抽象(抽象出Pizza类),然后撇开具体的类,在抽象层面拓展(设计PizzaStore),有几个原则可以帮助我们规范使用这个原则,这些并不是都要做到,只要尽力即可:

  • 变量不可以持有具体类的引用(如果使用new就会持有具体类的引用,可以使用工厂模式来避免)。
  • 不要让类派生自具体类(都是从抽象类或接口派生出来的)。
  • 不要覆盖基类中已经实现的方法。(这样会导致继承的复杂度上升,同时说明基类不合适被继承——基类中已实现的方法该由子类共享)

 

5.抽象工厂模式:

我们现在的场景是要在Pizza上采用不同的原料,针对不同的分店,首先我们先定义一个产生原料的工厂(抽象工厂类),我们有六种原料要供应:

 

public interface PizzaIngredientFactory {//抽象工厂的任务是定义一个负责创建一组产品的接口,在这个接口中的每个方法都负责创建一个具体的产品,利用抽象工厂的子类提供具体做法。 
    
public Dough createDough(); 
    public Sauce createSauce(); 
    public Cheese createCheese(); 
    public Veggies[] createVeggies(); 
    public Pepperoni createPepperoni(); 
    public Clams createClam(); 
}

 

(注:如果每个具体工厂类内部需要实现某种通用机制,这里就采用抽象类)

 

我们根据这一模式构建各地的分工厂(具体工厂类):

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,使用原料工厂提供的原料,这是一个抽象产品类:

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() { 
            } 
}

针对这一个基本的方式,我们构建具体的Pizza(这是具体产品类):

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

 

接着,我们要在店面上进行抽象(抽象工厂类):

 

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; 
    } 
}

 

在这个抽象的基础上我们开一家分店(具体工厂类):

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"); 
        } 
}

抽象工厂方法提供了一个接口(此例中为PizzaIngredientFactory),用于创建相关或者依赖的对象家族,而不需要明确指定具体类,每个家族成员(此例为NYPizzaIngredientFactory)都负责创建一个具体的产品。与工厂方法的区别在于:工厂方法使用继承,针对的是类,利用工厂方法创建对象要扩展一个类,并覆盖它的工厂方法(例如这里的createPizza)——用来创建对象,工厂方法的实质在于通过子类创建对象;抽象工厂方法使用的是组合(ingredientFactory),针对的是一族对象,要使用这个工厂要首先将其实例化,然后将它传入一些针对抽象类型所写的代码中,优点在于可以把一群相关的产品集合起来,具体的工厂都是由工厂方法(prepare)创建的(这就是工厂方法和抽象工厂方法的联系)。在这个例子中商店的实现是采用的工厂方法,而制作Pizza的原料相关的类是采用的抽象工厂方法。

 

总结起来,这三者如下:

==========================

 

简单工厂方法中,

首先包括一个“抽象产品类”(该类可以是接口Interface,也可以是实际的类Class,本例中是Pizza),所有需要的产品类都是该“抽象产品类”的子类(如果是接口的话,那么就是说所有产品类都继承了该接口),本例中为各种XXPizza。

 

另外还包含一个具体的工厂类(本例为SimplePizzaFactory),所有需要的产品类都是该类生成的产品类对象。生成产品类的方法,其内部一般是类似于switch的结构,根据输入的标志,选择创建不同类型的对象。由于不知道创建的对象到底是哪个类的,所以方法的返回值的类型是“抽象产品类”。譬如,Pizza createPizza(String type),type就是一个标志,返回的是Pizza这个抽象产品类。

 

==========================

 

工厂方法中,

首先包括一个抽象产品类(本例中是Pizza),可以派生出多个具体产品类(本例为XXXPizza),这个和简单工厂方法没有区别。 

 

另外还包含一个抽象工厂类(本例为PizzaStore),可以派生出具体工厂类(本例为XXXPizzaStore,这个与简单工厂方法中的具体工厂类没有区别), 每个具体工厂类(比如本例的ChicagoPizzaStore )可以根据输入标志创建一个具体产品类的实例。

 

较之简单工厂方法,工厂方法对于工厂类进行了抽象产生了一个抽象工厂类,通过这个抽象工厂类规定了一系列流程框架(orderPizza方法),另外,还将具体产品类的创建交给了具体的工厂类(createPizza在XXXPizza类中的实现)——也就是说,有多个具体工厂类相对应多个具体产品类,工厂类和产品类的耦合度下降。在本例中,如果不使用工厂方法,那么createPizza除了传入Pizza的类型还要传入商店的地点信息,譬如Chicago,这样的耦合度就会很大。

 

==========================

 

抽象工厂方法中,

 

首先,包括一个抽个产品类(本例中为Pizza),该抽象产品类可以派生出多个具体产品类(本例为XXXPizza),在每个具体产品类中以组合的形式将另一个抽象工厂类(本例为PizzaIngredientFactory )的引用包含进来,完成一群具体产品类的构建(XXXPizza的Prepare方法)。

 

另外还包含两个抽象工厂类,本例中PizzaStore,可以派生出多个具体工厂类XXXPizzaStore,这个与简单工厂方法中的具体工厂类没有区别,  每个具体工厂类可以创建一个具体产品类的实例(XXXPizza),但是这个具体产品类的实例比较特殊,是利用另一个抽象工厂类PizzaIngredientFactory 的具体工厂类创建的一群具体产品类而构建(XXXPizza的Prepare方法),请注意这个类才是抽象工厂方法的实质。

分享到:
评论

相关推荐

    HeadFirst设计模式学习笔记

    《HeadFirst设计模式学习笔记》是一份详尽的资料,旨在帮助读者深入理解并掌握设计模式这一编程领域的核心概念。设计模式是软件工程中的一种最佳实践,它在解决常见问题时提供了一种标准的解决方案,使得代码更易于...

    HeadFirst 设计模式学习笔记1--策略模式Demo

    《HeadFirst设计模式学习笔记1--策略模式Demo》 在软件工程中,设计模式是一种解决常见问题的标准方案,它提供了一种在特定情况下组织代码的模板。策略模式是设计模式中的一种行为模式,它允许在运行时选择算法或...

    HeadFirst 设计模式学习笔记3--装饰模式 Demo

    在“HeadFirst 设计模式学习笔记3--装饰模式 Demo”中,作者通过实例讲解了装饰模式的基本概念、结构和应用场景。这篇文章可能是从CSDN博客平台上的一个链接访问的,遗憾的是,由于我们当前无法直接访问该链接,所以...

    HeadFirst 设计模式学习笔记2--观察者模式 demo

    总的来说,HeadFirst设计模式的学习笔记2关于观察者模式的演示,旨在帮助开发者理解如何使用观察者模式来构建可扩展的系统。通过实际的代码示例,我们可以更深入地掌握这一模式,并将其应用到日常开发中,提升代码的...

    读书笔记:设计模式学习笔记和代码。《图解设计模式》《Head First 设计模式》.zip

    读书笔记:设计模式学习笔记和代码。《图解设计模式》《Head First 设计模式》

    Head.First 设计模式学习笔记.pdf

    ### Head.First 设计模式学习笔记知识点总结 #### 一、设计模式概述 设计模式是一种用于解决软件设计中常见问题的标准化方法。通过采用设计模式,开发者可以提高代码的复用性、灵活性和可维护性。《Head First 设计...

    Head First 设计模式学习笔记(十四)模式的组合使用

    在《Head First 设计模式学习笔记(十四)模式的组合使用》中,作者探讨了如何在实际编程中灵活地组合多种设计模式以解决复杂问题。这篇文章可能是基于《Head First 设计模式》这本书的一个章节,该书是设计模式领域...

    Head First 设计模式 扫描版

    《Head First 设计模式》是软件开发领域内一本广受欢迎的书籍,由Eric Freeman、Elisabeth Robson、Bert Bates和Kathy Sierra四位作者共同撰写。这本书以其独特的视觉风格和易于理解的教学方法,深入浅出地介绍了...

    Head First设计模式读书笔记-DesignPatterns.zip

    《Head First设计模式》是一本深受开发者喜爱的设计模式学习书籍,它以易懂且生动的方式介绍了23种经典设计模式。这些模式是软件工程中经过实践验证的最佳实践,旨在提高代码的可重用性、可读性和可维护性。下面,...

    HeadFirst设计模式笔记

    《HeadFirst设计模式笔记》是深入理解软件设计思想的一份宝贵资料,主要涵盖了设计模式的基础理论和实际应用。设计模式是软件开发中的经验总结,它为解决常见的编程问题提供了标准的解决方案,使得代码更具可读性、...

    基于Java语言的《Head First 设计模式》学习笔记及实战练习源码

    本项目为《Head First 设计模式》的Java语言学习笔记与实战练习源码集合,包含104个文件,主要包括88个Java源文件、12个Markdown文档、3个XML配置文件及少量其他辅助文件。内容涵盖设计模式的学习笔记以及相应的代码...

    Head First Design Pattern 学习笔记

    著名的《Head First Design Pattern》学习笔记,摘要这本书中的设计思路。由于书本过长,整理出笔记帮助回想起设计模式。文件是docx格式,只能由OFFICE Word 2007之后的版本打开,内附Visio类图文件。本文由个人整理...

    head first 设计模式

    通过以上对“Head First设计模式”书中可能涉及的设计模式的介绍,我们可以看出这本书是学习面向对象设计模式的绝佳资源。无论是初学者还是有一定经验的开发人员,都能从中受益匪浅。理解并熟练运用这些模式能够帮助...

    Head First Servlets & JSP 学习笔记

    以上只是《Head First Servlets & JSP》一书中的部分核心知识点,实际内容还包括过滤器、监听器、MVC设计模式、JSTL等更广泛的主题,旨在帮助读者全面理解和掌握Servlet和JSP技术。通过深入学习,开发者能够构建高效...

    head first C#学习笔记:如何创建事件

    本学习笔记将深入探讨如何在C#中创建事件,以实现一个棒球模拟系统的例子。在这个系统中,我们将关注投球手、观众和裁判的交互,当输入棒球的轨迹和距离时,这些对象会根据模拟结果做出相应的反应。 首先,理解事件...

    head_first_servlet&jsp学习笔记

    【Servlet&JSP基础知识】 ...以上是`head_first_servlet&jsp`学习笔记的主要知识点,涵盖了Servlet和JSP的基础、Web应用架构、MVC模式、会话管理和JSP编程等多个方面,为深入理解和实践Servlet与JSP开发奠定了基础。

    设计模式笔记及其源代码(java版)

    设计模式Head First学习笔记,以及使用java编写的设计模式源码,Java原生sdk实现23种设计模式

    head first策略者模式

    《Head First 策略者模式》学习笔记 策略者模式是一种行为设计模式,它使你能在运行时改变对象的行为。在软件开发中,我们常常遇到需要根据不同的情况执行不同算法的问题。策略者模式就是为了解决这类问题而生,它...

Global site tag (gtag.js) - Google Analytics