# 简单工厂

当看到 “new” 时,就会想到 “具体”,每当我们程序中 new 一个对象时,都会绑着一个具体类,代码绑着具体类会导致代码更脆弱,更缺乏弹性。

// Duck 父类还是要 new 一个具体的绿头鸭实例
        Duck duck = new MallardDuck();

当有一群相关的具体类时,类似下面代码。

Duck duck;
        
        if ( picnic ){
            duck = new MallardDuck();
        }else if ( hunting ){
            duck = new DecoyDuck();
        }else if ( inBathTub ){
            duck = new RubberDuck();
        }

这样的代码,一旦有变化或扩展,就必须打开这段代码进行检查和修改。系统会很难维护或更新,也容易犯错。

举个例子说明:

我们可以识别变化的方面,假设你有一个比萨店,作为店长,你可以这样写:

Pizza orderPizza( String type ){
        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;
    }

随着时间的改变,当前菜单肯定会变化,如新增某个种类比萨。那我们需要 new 一个新的 Pizza 实现。此时我们又要添加一个 if...else...,导致了无法对修改关闭。

此时我们可以使用封装。将这些创建对象的代码封装起来,迁移到 orderPizza () 方法外,用于专职创建比萨。

此时,我们可以称这个新对象为 “工厂”。将 orderPizza () 方法称为该对象的客户。客户只需要关心从比萨工厂得到了一个比萨,而且这个比萨实现了 Pizza 接口。

package com.example.pattern.factory;
 
/**
 * <p>
 * <code>SimplePizzaFactory</code>
 * </p>
 * Description: 比萨简单工厂
 *
 * @author Mcchu
 * @date 2018/1/16 14:59
 */
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;
    }
}

从表面上看,似乎只是把问题搬到了另一个对象罢了,问题依然存在,其实这里将代码抽离出来作为一个工厂,那么他此时不仅仅可以服务于上面的 orderPizza () 方法,就是说未来还可以在其他地方调用,服务于其他客户。

通常我们为了避免对象的创建来进行实例化,会利用静态方法定义一个简单的工厂,常称为静态工厂,这是一种技巧,但要注意,此时的缺点是不能通过继承来改变创建方法的行为。

下面我们重做 PizzaStore 类,即改变我们的客户代码,依仗我们的工厂来创建比萨。

package com.example.pattern.factory;
 
/**
 * <p>
 * <code>Test</code>
 * </p>
 * Description:
 *
 * @author Mcchu
 * @date 2018/1/16 14:28
 */
public class PizzaStore {
 
    SimplePizzaFactory factory;
    
    public PizzaStore( SimplePizzaFactory factory ){
        this.factory = factory;
    }
 
    Pizza orderPizza( String type ){
        Pizza pizza;
 
        pizza = factory.createPizza(type);
 
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
 
        return pizza;
    }
    
    // ...  ...
}

此时 SimplePizzaStore 就是一个简单工厂,注意,简单工厂其实不是一个设计模式,反而更像是一种编程习惯,不要误认为是 “工厂模式”

附:

简单工厂说明图:来自 CSDN

注意上面只给出了客户和简单工厂,抽象产品其实就是指上面各种具体比萨的所实现的抽象类 Pizza。

在这里说明一下:在设计模式中,所谓的 “实现一个接口” 并 “不一定” 表示 “写一个类,并利用 implement 关键字来实现某个 java 接口”。“实现一个接口” 泛指 “实现某个超类型(可以是类或接口的某个方法)”

实际应用中,抽象可能是多层次的,如