# 例子

加盟比萨店,在上篇简单工厂的介绍中,假设你已经创建了一个比萨店,有了几种拿手的比萨产品,现在你想要在其他地方,比如上海和深圳新开两家加盟店,但是其中上海的加盟店希望工厂能制造上海风味的比萨,而深圳加盟店希望工厂能提供深圳风味的比萨,而你又想要所有加盟店都能利用你的代码,好让比萨的流程能保持一致不变。

考虑到上面的区域因素,我们这里有一种做法:写出两种不同的工厂 ShanghaiPizzaFactory、ShenzhenPizzaFactory

此时调用工厂的代码如下:

public static void main(String[] args) {
        ShanghaiPizzaFactory shFactory = new ShanghaiPizzaFactory();
        PizzaStore shStore = new PizzaStore(shFactory);
        shStore.orderPizza("Veggie");
 
        ShenzhenPizzaFactory szFactory = new ShenzhenPizzaFactory();
        PizzaStore szStore = new PizzaStore(szFactory);
        szStore.orderPizza("Veggie");
    }

但是假如说你想要多一些质量控制,虽然这两个加盟店的确是采用你的工厂创建比萨,但是其他部分却开始采用他们自创的流程:比如烘烤的做法有些差异、不要切片、使用其他厂商的盒子等。此时上面的做法就不具有弹性了。

此时我们可以把 createPizza () 方法放回到 PizzaStore 中,不过要把他设定为 abstract,然后为每个区域的加盟店创建一个 PizzaStore 的子类。

public abstract class PizzaStore {
 
    public Pizza orderPizza( String type ){
        Pizza pizza;
 
        pizza = createPizza(type);
 
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
 
        return pizza;
    }
 
    // 实例化比萨的责任被移动到一个方法中,此方法就如同是一个工厂,此时这个方法就可以叫工厂方法
    abstract Pizza createPizza( String type );
}

我们先来剖析一下上面的工厂方法:

  • abstract:工厂方法是抽象的,所以依赖子类来处理对象的创建;
  • Pizza:工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值;
  • createPizza:工厂方法将客户(也就是超类中的代码 orderPizza () 和实际创建的具体产品代码分隔开来);
  • type:工厂方法可能需要参数(也可能不需要)来指定所要的产品;

此时这个 PizzaStore 作为超类,让每个域类型(ShanghaiPizzaStore、ShenzhenPizzaStore)都继承这个 PizzaStore,就是说让每个子类决定如何制造比萨,把 createPizza () 这个方法交给子类去处理。

此时,如果想要在合肥新开一个比萨加盟店,只需要继承 PizzaStore 就可以了。

public class HefeiPizzaStore extends PizzaStore {
    
    @Override
    Pizza createPizza(String type) {
        if ( type.equals("cheese") ){
            return new HFStyleCheesePizza();
        }else if ( type.equals("veggie") ){
            return new HFStyleVeggiePizza();
        }else if ( type.equals("clam") ){
            return new HFStyleClamPizza();
        }else if ( type.equals("pepperoni") ){
            return new HFStylePepperoniPizza();
        }else {
            return null;
        }
    }
}

比萨店有了,现在我们来给合肥比萨店创建一个具体的 Pizza 产品。首先定义 Pizza 基类。

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

然后是具体 Pizza 类

public class HFStyleCheesePizza extends Pizza {
 
    public HFStyleCheesePizza(){
        name = "HF Style Sauce and Cheese Pizza";
        dough = "Thin Crust Dough";
        sauce = "Marinara Sauce";
        
        toppings.add("Grated Reggiano Cheese");
    }
    
    @Override
    void cut(){
        System.out.println("Cutting the pizza into square slices");
    }
 
    // ...  ...
}

此时客人 Jalen 来了,他订了一份合肥的比萨。

public class Test {
 
    public static void main(String[] args) {
        PizzaStore hfStore = new HefeiPizzaStore();
 
        Pizza pizza = hfStore.orderPizza("cheese");
        System.out.println("Jalen order a " + pizza.getName() );
    }
}

输出结果:

Preparing HF Style Sauce and Cheese Pizza
Tossing dough...
Adding sauce...
Adding toppings: 
    Grated Reggiano Cheese
Bake for 25 minutes at 350
Cutting the pizza into square slices
Place pizza in official PizzaStore box
Jalen order a HF Style Sauce and Cheese Pizza

# 例子解析

上面的例子设计就是运用了工厂方法模式,所有工厂模式都用来封装对象的创建。

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

上面例子中,有两个基类以供子类继承:创建者(Creator)类 - PizzaStore,产品类 - Pizza;

可以将他们认为是平行的类层级,即他们都有抽象类,而抽象类都有很多具体的子类,每个子类都有特定的实现。

# 认识工厂方法模式(Factory Method Pattern)

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