工厂方法模式,是工厂模式的一种,它对简单工厂模式进行进一步抽象化。它将工厂和产品进行抽象,抽象出来的工厂负责定义创建产品的规范,而抽象的产品负责定义产品规范,然后通过具体的工厂实现类来创建具体的产品。

1. 简介

工厂方法模式(Factory Method Pattern),它的定义如下:定义一个创建产品对象的工厂接口,它负责定义创建产品的方法,而具体的产品创建工作推迟到具体工厂类当中来完成,具体工厂类来角色如何创建产品。

优点
  1. 客户端(产品使用者)和产品的创建过程相分离,客户端无需关心产品的创建过程

  2. 系统增加新的产品时,只需要添加具体产品类和创建它的具体工厂实现即可,无需修改原有代码,满足开闭原则

缺点

每个具体的产品都对应了具体的工厂,系统复杂度增加。

2. 结构

简单工厂模式在新加产品的时候会违背开闭原则,而工厂方法模式对其进行了改进,增加了抽象工厂和抽象产品。

工厂方法模式由抽象工厂、具体工厂、抽象产品和具体产品构成,其类图如下:

factory method pattern class
  • 抽象工厂(AbstractFactory):抽象了创建产品的方法,例如 createProduct(),但是不负责具体实现产品创建过程;这里的抽象工厂一般是接口,也可以是抽象类

  • 具体工厂(ConcreteFactory):抽象工厂的具体实现类,实现了抽象工厂定义的产品创建方法

  • 抽象产品(AbstractProduct):就是图中的Product,抽象产品对工厂生产的产品进行了抽象,定义了产品的规范,如产品的功能、参数、属性等;同样的,抽象产品也可以是抽象类或接口

  • 具体产品(ConcreteProduct):抽象产品的具体实现,由具体工厂进行创建,通常,具体产品与具体工厂成一一对应关系

3. 示例

继续使用 上一篇 生产牛奶的例子。商品售卖各种牛奶,如纯牛奶、酸奶、高钙奶等等,前边使用的是简单工厂模式。现在,我们将其改为工厂方法模式。

类的设计如下:

factory method pattern demo

这里,抽象的产品是 Milk,抽象的工厂是 MilkFactory,具体的工厂是 PureMilkFactory 等,他们负责生产某一种具体的产品,如 PureMilkFactory 负责生产 PureMilk。商店 Store 不用关心具体的产品是什么,它只需要依赖抽象的工厂和产品即可。

部分关键代码如下:

抽象工厂
interface MilkFactory {
    Milk createMilk();
}
抽象产品
interface Milk {
    String getName();
}
具体产品
class PureMilk implements Milk {
    @Override
    public String getName() {
        return "伊利纯牛奶";
    }
}
具体工厂
class PureMilkFactory implements MilkFactory {
    @Override
    public Milk createMilk() {
        return new PureMilk(); (1)
    }
}
1具体工厂生产具体的产品,他们之间一一对应。

现在,商店开始出售牛奶了:

商店
class Store {
    private MilkFactory milkFactory; (1)

    public Milk sale() { (2)
        System.out.println("顾客购买牛奶");
        System.out.println("  > 生产牛奶");
        Milk milk = milkFactory.createMilk();
        System.out.println("  > 收款");
        System.out.println("  > 拿货");
        System.out.println("成功售出了" + milk.getName());
        return milk;
    }

    public void setMilkFactory(MilkFactory milkFactory) {
        this.milkFactory = milkFactory;
    }
}
1Store 依赖抽象工厂,用来生产具体的商品
2Store 其实也不关心具体的牛奶,因为他是工厂生产的,它只依赖抽象产品即可

然后,用户只需要告诉 Store 我需要买什么牛奶,然后客户端设置不同的具体工厂即可:

public class FactoryMethodPattern {
    public static void main(String[] args) {
        Store store = new Store();
        // 买纯牛奶
        store.setMilkFactory(new PureMilkFactory());
        store.sale();

        // 买酸奶
        store.setMilkFactory(new YogurtFactory());
        store.sale();

        // 买高钙奶
        store.setMilkFactory(new HighCalciumMilkFactory());
        store.sale();
    }
}

使用工厂方法模式之后,如果还要添加新的牛奶,那么只需要做两处改动:

  1. 添加具体的产品实现,实现 Milk 即可

  2. 添加具体的工厂实现,生产新的牛奶

原有代码无需做任何改动,只要客户端代码设置了新的具体工厂,就可以出售新的牛奶了,符合软件设计的开闭原则。

4. 总结

工程方法模式,对简单工厂进行了改进,遵循开闭原则,并抽象了四个具体的角色:抽象工厂、抽象产品、具体工厂、具体产品。通常,具体工厂和具体产品时一一对应的,但是,也可能存在一个工厂生产多种产品的形式。设计模式重要的是其核心思想,而在具体设计表现形式上可能存在多种变化。

本文示例代码见: Github


相关阅读