码迷,mamicode.com
首页 > 其他好文 > 详细

工厂模式与抽象工厂模式小览(一)

时间:2015-08-06 11:11:35      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:工厂模式   c++   java   设计模式   软件架构   

一、文章来由

一天看完 hf设计模式 中的工厂模式,略微还有点消化不了,于是打算好好探究一下。其实不仅仅是这两个模式之间容易混淆,他们各自的定义也是挺绕的,下面我就仔细回过头翻书+查阅资料,重新捋一捋,研究一下这两个“工厂”。同时还是保持一个开放的心态,设计模式博大精深,很难一文以蔽之,于是《xx小览》系列文章又添新作。

Ps. 鉴于类容过长,我分成两部分来写,第一部分主要是自己对于看 hf设计模式 的总结,第二部分是来自书本和查找资料详细对比二者的不同~~

二、简单工厂模式

这是最简单的一种工厂,所做的事情只有一个:

原来所有的 new 操作都在 PizzaStore 主类的 orderPizza 方法中,现在抽离出来,放到简单工厂中,且这个创建方法通常是静态的,见下图也有说。

所以,简单工厂说白了就是一个代理~~
目的也很简单,只是为了如果要添加一种 pizza 的时候不要修改主类就好(也就是所谓的对修改关闭)

PS. 代理,就是不在类中直接定义方法,而使用其它类的成员变量 调用方法(本例中是简单工厂代理了 createPizza() 方法)
PS2. 工厂方法跟模式没有什么联系,即使简单工厂里面也有工厂方法

严格来说,简单工厂方法都不能称作模式,反而更像一种编程习惯,但是经常用,而且总被人误认为一种模式,所以叫做模式了,其实是伪模式~~

技术分享

详见代码:

////以下源代码顺序:【PizzaStore.java】->【SimplePizzaFactory.java】->【Pizza.java】->【CheesePizza.java】->【main】

///PizzaStore.java
package headfirst.factory.pizzas;

public class PizzaStore {
    SimplePizzaFactory factory;

    public PizzaStore(SimplePizzaFactory factory) { 
        this.factory = factory;
    }

    public Pizza orderPizza(String type) {
        Pizza pizza;

        pizza = factory.createPizza(type);

        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();

        return pizza;
    }

}

///SimplePizzaFactory.java
package headfirst.factory.pizzas;

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.java
package headfirst.factory.pizzas;

import java.util.ArrayList;

abstract public class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList toppings = new ArrayList();

    public String getName() {
        return name;
    }

    public void prepare() {
        System.out.println("Preparing " + name);
    }

    public void bake() {
        System.out.println("Baking " + name);
    }

    public void cut() {
        System.out.println("Cutting " + name);
    }

    public void box() {
        System.out.println("Boxing " + name);
    }

    public String toString() {
        // code to display pizza name and ingredients
        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();
    }
}

///CheesePizza.java
package headfirst.factory.pizzas;

public class CheesePizza extends Pizza {
    public CheesePizza() {
        name = "Cheese Pizza";
        dough = "Regular Crust";
        sauce = "Marinara Pizza Sauce";
        toppings.add("Fresh Mozzarella");
        toppings.add("Parmesan");
    }
}

///PizzaTestDrive.java【main】
package headfirst.factory.pizzas;

public class PizzaTestDrive {

    public static void main(String[] args) {
        SimplePizzaFactory factory = new SimplePizzaFactory();
        PizzaStore store = new PizzaStore(factory);

        Pizza pizza = store.orderPizza("cheese");
        System.out.println("We ordered a " + pizza.getName() + "\n");

        pizza = store.orderPizza("veggie");
        System.out.println("We ordered a " + pizza.getName() + "\n");
    }
}

还是很清晰的~~

三、工厂模式

终于进入重头戏之一了,关于这个问题呢,hf设计模式 举了一个例子—加盟 pizza 店~~这样就有所谓的 New York Pizza、Chicago Pizza 和 California Pizza,如果用简单工厂,就必须有三个简单工厂来生产, New York Pizza工厂、Chicago Pizza工厂 和 California Pizza工厂,显然不可取。。。

工厂方法的核心:允许子类做决定

就是让 PizzaStore 的各个子类复制定义自己的createPizza() 方法。下面的PizzaStore是抽象类,createPizza() 方法也是抽象的~~

技术分享

这样做神奇的地方在于,对于super class 的 PizzaStore , 它的方法 orderPizza() 调用了 Pizza 的准备、烘烤、切片、装盒一系列方法,但是 Pizza 却是抽象的,orderPizza() 并不知道是哪些实际的Pizza ,这就是所谓的解耦

PizzaStore 也会调用它的抽象方法 createPizza() 取得Pizza对象,但是究竟会得到那种Pizza,对他来说也是透明的~~是“顾客”决定到哪个店买,才决定了是哪种 Pizza

上图中的 createPizza() 方法就是一个工厂方法,用来给不同的加盟店继承并去实现。createPizza() 方法的调用是在 orderPizza() 方法中的~~

接下来差不多该上代码了~~

注意,在下面的类中,工厂模式的 Pizza 主类以及 Pizza 的子类和简单工厂模式并没有什么不同~~

////以下源代码顺序:【PizzaStore.java】->【NYPizzaStore.java】->【Pizza.java】->【CheesePizza.java】->【main】

///PizzaStore.java
package headfirst.factory.pizzafm;

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

///NYPizzaStore.java
package headfirst.factory.pizzafm;

public class NYPizzaStore extends PizzaStore {

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

///Pizza.java
package headfirst.factory.pizzafm;

import java.util.ArrayList;

public abstract class Pizza {
    String name;
    String dough;
    String sauce;
    ArrayList toppings = new 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();
    }
}

///NYStyleCheesePizza.java
package headfirst.factory.pizzafm;

public class NYStyleCheesePizza extends Pizza {

    public NYStyleCheesePizza() { 
        name = "NY Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";

        toppings.add("Grated Reggiano Cheese");
    }
}

///PizzaTestDrive.java【main】
package headfirst.factory.pizzafm;

public class PizzaTestDrive {

    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a " + pizza.getName() + "\n");

        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel ordered a " + pizza.getName() + "\n");
    }
}

整个过程是这样的:
技术分享

工厂模式的创建者类和产品类是平行的类层级~~
技术分享

另外,这里涉及一个很高逼的原则—依赖倒置原则

要依赖抽象,不要依赖具体类

再具体一点就是以下原则:

1、变量不开源持有具体类的引用。(如果使用new,就会持有具体类的引用,改用工厂方法可以避开这种做法)

2、不要让类派生自具体类。(因为派生自具体类,就会依赖具体类)

3、不要覆盖基类中已实现的方法。(如果基类已实现,就说明不是一个很适合继承的抽象类,基类中已实现的方法,应该由所有子类共享)

工厂模式 over

四、抽象工厂模式

这个是重中之重了~~

对于抽象工厂模式,hf设计模式 又想了另一个很好的例子,但是这次不只是 NY 或是 Chicago 简单的地域变换(仅仅是地区问题,就可以只使用工厂模式解决),这个例子是关于原料要是统一的工厂发配,但是每个地区的口味不同,原料也不一样~~

技术分享

注意:以下箭头标注的,每一个地方的原料是固定的,如果这些 sauce 都一样
技术分享

所以关键在于:

这些 Pizza 都是采用相同的组件制造(有酱料、干酪、蛤蜊。。。),但每个区域对于这些组件有不同的制造(也就是说有 NY 原料工厂、Chicago 原料工厂。。。)

感悟:重点在于,是一组抽象的制造,然后子类继承并实现这一组的new

而每个具体的原料工厂就生产当地必须的原料 即可。

其实说穿了上面这一点,感觉抽象工厂也就差不多了~~

于是。。。。。。。。。。。是时候上源码了~~

特别注意:这个例子里面抽象工厂的 Pizza 类改变比较大,因为 Pizza 的原料是各个地方生产的,所以 Pizza 主类的 prepare() 方法是抽象的,而对于各种 Pizza 子类,实现 prepare() 方法时,也只是简单的使用了下面这句~~

sause = ingredientFactory.createSauce();

Pizza 类根本不关心这些原料,它只关心如何制作。所以 Pizza 和区域原料之间解耦,于是不会出现工厂模式中的类似 NYStyleCheesePizza 的类了。

////以下源代码顺序:【PizzaStore.java】->【NYPizzaStore.java】->【PizzaIngredientFactory.java】->【NYPizzaIngredientFactory.java】->【Pizza.java】->【CheesePizza.java】->【Cheese.java】->【MozzarellaCheese.java】->【main】

///PizzaStore.java(PizzaStore 没什么变化)
package headfirst.factory.pizzaaf;

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

///NYPizzaStore.java
package headfirst.factory.pizzaaf;

public class NYPizzaStore extends PizzaStore {

    protected Pizza createPizza(String item) {
        Pizza pizza = null;

        ///各地的 PizzaStore 从原料工厂进货,实例化 NY 原料工厂
        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;
    }
}

///PizzaIngredientFactory.java【原料工厂抽象类】
package headfirst.factory.pizzaaf;

public interface PizzaIngredientFactory {

    public Dough createDough();
    public Sauce createSauce();
    public Cheese createCheese();
    public Veggies[] createVeggies();
    public Pepperoni createPepperoni();
    public Clams createClam();

}

///NYPizzaIngredientFactory.java
package headfirst.factory.pizzaaf;

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.java【Pizza 抽象类,注意 prepare() 方法是抽象的】
package headfirst.factory.pizzaaf;

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

///CheesePizza.java
package headfirst.factory.pizzaaf;

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

///Cheese.java
package headfirst.factory.pizzaaf;

public interface Cheese {
    public String toString();
}

///MozzarellaCheese.java
package headfirst.factory.pizzaaf;

public class MozzarellaCheese implements Cheese {

    public String toString() {
        return "Shredded Mozzarella";
    }
}

///PizzaTestDrive.java【main】
package headfirst.factory.pizzaaf;

public class PizzaTestDrive {

    public static void main(String[] args) {
        PizzaStore nyStore = new NYPizzaStore();
        PizzaStore chicagoStore = new ChicagoPizzaStore();

        Pizza pizza = nyStore.orderPizza("cheese");
        System.out.println("Ethan ordered a " + pizza + "\n");

        pizza = chicagoStore.orderPizza("cheese");
        System.out.println("Joel ordered a " + pizza + "\n");

        pizza = nyStore.orderPizza("clam");
        System.out.println("Ethan ordered a " + pizza + "\n");

        pizza = chicagoStore.orderPizza("clam");
        System.out.println("Joel ordered a " + pizza + "\n");
    }
}

版权声明:欢迎转载,注明出处就好!如果不喜欢请留言说明原因再踩哦,谢谢,我也可以知道原因,不断进步!!

工厂模式与抽象工厂模式小览(一)

标签:工厂模式   c++   java   设计模式   软件架构   

原文地址:http://blog.csdn.net/scythe666/article/details/47304985

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!