Head First 设计模式(三)工厂模式

工厂模式可以分为三种:
简单工厂模式工厂方法模式抽象工厂模式

简单工厂模式

简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。由于经常被使用,所以被许多开发人员误认为是“工厂模式”。

以披萨店“订披萨”为例,每个客人到披萨店订不同的披萨,需要生产出不同的披萨来满足客人的要求。

披萨类:

public interface Pizza {    
    /**烘烤*/
    public void bake();
    /**切片*/
    public void cut();
    /**打包*/
    public void box();
}

/**
 * 奶酪披萨
 */
public class CheesePizza implements Pizza{

    @Override
    public void bake() {
    }

    @Override
    public void cut() {
    }

    @Override
    public void box() {     
    }
}


/**
 * 榴莲披萨
 */
public class DurianPizza implements Pizza{

    @Override
    public void bake() {        
    }

    @Override
    public void cut() {     
    }

    @Override
    public void box() {     
    }
}


/**
 * 蔬菜披萨
 */
public class VeggiePizza implements Pizza{

    @Override
    public void bake() {        
    }

    @Override
    public void cut() {     
    }

    @Override
    public void box() {     
    }
}

没有工厂类之前,披萨店订披萨:


/**
 * 披萨店
 */
public class PizzaStore {
    public Pizza orderPizza(String type) {
        Pizza pizza = createPizza(type);
        //披萨的准备
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    public Pizza createPizza(String type){
        if("cheese".equals(type))
            return new CheesePizza();
        else if("veggie".equals(type)){
            return new VeggiePizza();
        }
        else if("durian".equals(type)){
            return new DurianPizza();
        }
        else {
            return null;
        }
    }
}

缺点:披萨种类每次更改,所有的披萨店(假设还有PizzaStore2PizzaStore3类)代码都要改变。所以我们运用之前学到的设计原则:

找出应用中可能需要变化之处,把他们独立出来。

将创建披萨的部分代码独立出来,成为工厂:


/**
 * 简单工厂模式
 */
public class SimpleFactory {

    public Pizza createPizza(String type){
        if("cheese".equals(type))
            return new CheesePizza();
        else if("veggie".equals(type)){
            return new VeggiePizza();
        }
        else if("durian".equals(type)){
            return new DurianPizza();
        }
        else {
            return null;
        }
    }
}

工厂类之后,披萨店订披萨:

/**
 * 披萨店
 */
publicclass PizzaStore {
    private SimpleFactory simpleFactory = new SimpleFactory();

    public Pizza orderPizza(String type) {
        //通过工厂获取披萨
        Pizza pizza = simpleFactory.createPizza(type);
        //披萨的准备
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}

简单工厂适用于产品不复杂的情况下,为了方便,还可以将获取产品的方法设置为静态(静态工厂),但这样不能通过继承来改变创建产品的行为。

工厂方法模式

当产品类别变得复杂,例如披萨又根据生产地的不同而有了不同的口味:纽约风味、芝加哥风味。

这样简单工厂制作产品的代码就得改成:

public Pizza createPizza(String local,String type){
        //如果店所在地为纽约,则生成纽约风味披萨
        if("NY".equals(local)){
            if("cheese".equals(type))
                return new NYCheesePizza();
            else if("veggie".equals(type)){
                return new NYVeggiePizza();
            }
            else if("durian".equals(type)){
                return new NYDurianPizza();
            }
            else{
                return null;
            }
        }
        //如果店所在地为芝加哥,则生成芝加哥风味披萨
        else if("Chicago".equals(local)){
            if("cheese".equals(type))
                return new ChicagoCheesePizza();
            else if("veggie".equals(type)){
                return new ChicagoVeggiePizza();
            }
            else if("durian".equals(type)){
                return new ChicagoDurianPizza();
            }
            else{
                return null;
            }

        }
        else {
            return null;
        }
    }

每当有新的产品出现,都得修改工厂类的方法。随着产品的增加,这个方法会越来越庞大和不好维护;另外,这也违反了我们之前学到的一个设计原则:

类应该对扩展开发,对修改关闭

如何改变?这里推出工厂方法模式

定义:

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

用我的理解翻译就是:

根据产品的类别的不同,定义出不同的产品创造工厂方法。需要创造产品的地方引用这些方法。

代码:

仍用披萨来示例,现在披萨店分为了纽约披萨店、芝加哥披萨店,它们各自需要创建各种地区风味的披萨。

我们将工厂类创造披萨的方法放在披萨店中作为“工厂方法”来实现(可以将此时的披萨店看成一个制作产品的工厂)。

首先,所有的披萨店都有自己创造披萨的“工厂方法”,所以我们定义一个抽象父类,里面定义了创造披萨的方法,但没有实现过程:


/**
 * 披萨店
 */
public abstract class PizzaStore {
    public Pizza orderPizza(String type) {
        //通过工厂方法获取披萨
        Pizza pizza = createPizza(type);
        //披萨的准备
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }

    /**
     * 获取披萨的工厂方法
     */
    abstract Pizza createPizza(String type);
}

再定义生成不同风味披萨,处在不同地区的披萨店:


/**
 * 纽约披萨店
 */
public class NYPizzaStore extends PizzaStore{

    //返回纽约风味的各种类型披萨
    @Override
    Pizza createPizza(String type) {
        if("cheese".equals(type))
            return new NYCheesePizza();
        else if("veggie".equals(type)){
            return new NYVeggiePizza();
        }
        else if("durian".equals(type)){
            return new NYDurianPizza();
        }
        else {
            return null;
        }
    }

}


/**
 * 芝加哥披萨店
 */
public class ChicagoPizzaStore extends PizzaStore{

    //返回芝加哥风味的各种类型披萨
    @Override
    Pizza createPizza(String type) {
        if("cheese".equals(type))
            return new ChicagoCheesePizza();
        else if("veggie".equals(type)){
            return new ChicagoVeggiePizza();
        }
        else if("durian".equals(type)){
            return new ChicagoDurianPizza();
        }
        else {
            return null;
        }
    }

}

工厂方法模式的框架如下:

披萨店就是Creator,而披萨对应Product值得注意的是,网上搜索的工厂方法模式,Creator对应都是Factory类,实现同一接口的Factory子类中实现不同的工厂方法。看上去好像与本书的代码有区别,实际,你把披萨店看成一个Factory就可以理解了

抽象工厂模式

定义:

抽象工厂模式提供一个接口,用于创建相关或依赖的家族,但不需要明确指定具体类

当产品进一步增多,且互相之间开始有关联时,我们就需要用抽象工厂模式了。

代码:

下面继续用披萨举例,让我们把披萨细节化,假设一个芝士披萨需要四个原料做成:面团(dough)、酱料(sauce)、蔬菜(Veggies)、和芝士(cheese):

我们先定义一个披萨抽象类,把披萨需要的所有原料(假设有四种)都“放进去”

public abstract class Pizza {   
    /**面团*/
    Dough dough;
    /**酱料*/
    Sauce sauce;
    /**芝士*/
    Cheese cheese;
    /**蔬菜*/
    Veggies veggies;

    /**准备披萨*/
    public abstract void prepare();
    /**烘烤*/
    public abstract void bake();
    /**切片*/
    public  abstract void cut();
    /**打包*/
    public abstract void box();
}

现在我们改变思维,制作一个不同的披萨需要不同的原料,所以我们定义一个披萨原理工厂接口:

/**
 * 披萨原料工厂
 * @author wangsz
 */
public interface PizzaIngredientFactory {
    /**
     * 制作面团的工厂方法
     */
    public Dough createDough();
    /**
     * 制作酱油的工厂方法
     */
    public Sauce createSauce();
    /**
     * 制作芝士的工厂方法
     */
    public Cheese createCheese();
    /**
     * 制作蔬菜的工厂方法
     */
    public Veggies createVeggies();
}

不同风味的披萨实际就是不同原料的披萨,所以我们定义不同的披萨原料工厂:

/**
 * 纽约风味披萨原料工厂
 * @author wangsz
 */
public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{

    @Override
    public Dough createDough() {
        return new ChicagoDough();
    }

    @Override
    public Sauce createSauce() {
        return new ChicagoSauce();
    }

    @Override
    public Cheese createCheese() {
        return new ChicagoCheese();
    }

    @Override
    public Veggies createVeggies() {
        return new ChicagoVeggies();
    }

}

接着我们以芝士披萨为例,假设它需要三种原料:面团(dough)、酱料(sauce)、和芝士(cheese),那么不同风味的芝士披萨由不同的芝士原料工厂提供原料,我们只需要指定不同的工厂,则制作出不同风味的芝士披萨

/**
 * 芝士披萨
 */
public class CheesePizza extends Pizza {

    PizzaIngredientFactory pizzaIngredientFactory;

    public CheesePizza(PizzaIngredientFactory pizzaIngredientFactory) {
        this.pizzaIngredientFactory = pizzaIngredientFactory;
    }

    @Override
    public void prepare() {
        this.dough = pizzaIngredientFactory.createDough();
        this.sauce = pizzaIngredientFactory.createSauce();
        this.cheese = pizzaIngredientFactory.createCheese();
    }

    @Override
    public void bake() {
    }

    @Override
    public void cut() {
    }

    @Override
    public void box() {
    }

}

最后我们看一下,现在纽约披萨店制作出披萨的方法

//定义原料工厂为纽约披萨原料工厂
    PizzaIngredientFactory pizzaIngredientFactory = new NYPizzaIngredientFactory();
    //返回纽约风味的各种类型披萨
    @Override
    Pizza createPizza(String type) {
        if("cheese".equals(type))
            return new CheesePizza(pizzaIngredientFactory);
        else if("veggie".equals(type)){
            return new VeggiePizza(pizzaIngredientFactory);
        }
        else if("durian".equals(type)){
            return new DurianPizza(pizzaIngredientFactory);
        }
        else {
            return null;
        }
    }

抽象工厂的框架如下:

抽象工厂和工厂方法模式的区别

  1. 工厂方法模式是都是生产一种产品,而抽象工厂是多个相关产品
  2. 工厂方法模式通过基础实现,抽象工厂模式通过对象的组合
  3. 对于扩展(如新增加一个产品),工厂方法模式可以通过增加一种新继承的工厂类实现;而抽象工厂模式不得不修改接口代码,由此带来大量子类的更改……

猜你喜欢

转载自blog.csdn.net/z55887/article/details/68953848
今日推荐