工厂方法模式

2021-01-26 设计模式架构设计

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。本文会先从简单方法模式入手,带你掌握工厂方法模式,以及下一篇的抽象工厂模式。

# 简单工厂模式

适用场景

  • 工厂类负责创建的对象比较少
  • 客户端(应用层)只知道传入工厂类的参数对于如何创建对象(逻辑)不关心

# 优缺点

  • 优点:只需要传入一个正确的参数,就可以获取你所需要的对象而无须知道其创建细节
  • 缺点:工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,违背开闭原则

# 实现步骤

  1. 创建抽象产品类,里面有产品的抽象方法,由具体的产品类去实现
  2. 创建具体产品类,继承了他们的父类,并实现具体方法
  3. 创建工厂类,提供了一个静态方法createXXX用来生产产品,只需要传入你想产品名称

# 具体实现

抽象支付类

public interface Pay {
    void unifiedorder();
}

具体实现类,支付宝支付

public class AliPay implements Pay{
    @Override
    public void unifiedorder() {
        System.out.println("支付宝支付 统一下单接口");
    }
}

具体实现类,微信支付

public class WechatPay implements Pay{
    @Override
    public void unifiedorder() {
        System.out.println("微信支付 统一下单接口");
    }
}

创建简单工厂类

public class SimplePayFactory {

    /**
     * 根据参数返回对应的支付对象
     * @param payType 参数
     * @return 具体的支付对象
     */
    public static Pay createPay(String payType) {
        if (payType == null) {
            return null;
        }

        if (payType.equalsIgnoreCase("WECHAT_PAY")) {
            return new WechatPay();
        } else if (payType.equalsIgnoreCase("ALI_PAY")) {
            return new AliPay();
        }
        return null;
    }
}

使用:

public class Main {
    public static void main(String[] args) {
        Pay pay = SimplePayFactory.createPay("WECHAT_PAY");
        pay.unifiedorder();
    }
}

# 小结

  • 优点:将对象的创建和对象本身业务处理分离可以降低系统的耦合度,使得两者修改起来都相对容易。
  • 缺点:
    1. 工厂类的职责相对过重,增加新的产品需要修改工厂类的判断逻辑,这一点与开闭原则是相违背
    2. 即开闭原则(Open ClosePrinciple)对扩展开放,对修改关闭,程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果
    3. 将会增加系统中类的个数,在一定程度上增加了系统的复杂度和理解难度,不利于系统的扩展和维护,创建简单对象就不用模式

# 抛砖引玉

假设你正在开发一款物流管理应用。 最初版本只能处理卡车运输, 因此大部分代码都在位于名为 卡车的类中。一段时间后, 这款应用变得极受欢迎。 你每天都能收到十几次来自海运公司的请求, 希望应用能够支持海上物流功能。如果代码其余部分与现有类已经存在耦合关系, 那么向程序中添加新类其实并没有那么容易。

在程序中新增一个运输类会遇到问题

这可是个好消息。 但是代码问题该如何处理呢? 目前, 大部分代码都与 卡车类相关。 在程序中添加 轮船类需要修改全部代码。 更糟糕的是, 如果你以后需要在程序中支持另外一种运输方式, 很可能需要再次对这些代码进行大幅修改。

最后, 你将不得不编写繁复的代码, 根据不同的运输对象类, 在应用中进行不同的处理。

# 工厂方法模式

  • 又称工厂模式,是对简单工厂模式的进一步抽象化,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则
  • 通过工厂父类定义负责创建产品的公共接口,通过子类来确定所需要创建的类型
  • 相比简单工厂而言,此种方法具有更多的可扩展性和复用性,同时也增强了代码的可读性
  • 将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定应该实例化哪一个类。

工厂方法模式图解: 工厂方法模式图解

核心组成

  • IProduct:抽象产品类,描述所有实例所共有的公共接口

  • Product:具体产品类,实现抽象产品类的接口,工厂类创建对象,如果有多个需要定义多个

  • IFactory:抽象工厂类,描述具体工厂的公共接口

  • Factory:具体工场类,实现创建产品类对象,实现抽象工厂类的接口,如果有多个需要定义多个

# 具体实现

Pay接口、AliPay和WechatPay类不变,新建工厂

public interface PayFactory {
    Pay getPay();
}

支付宝工厂

public class AliPayFactory implements PayFactory {

    @Override
    public Pay getPay() {
        return new AliPay();
    }
}

微信工厂

public class WechatPayFactory implements PayFactory {

    @Override
    public Pay getPay() {
        return new WechatPay();
    }
}

使用:

public class Main {
    public static void main(String[] args) {
        PayFactory payFactory = new AliPayFactory();
        Pay pay = payFactory.getPay();
        pay.unifiedorder();
    }
}

# 优点:

  1. 符合开闭原则,增加一个产品类,只需要实现其他具体的产品类和具体的工厂类;

  2. 符合单一职责原则,每个工厂只负责生产对应的产品

  3. 使用者只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则

    • 迪米特法则:最少知道原则,实体应当尽量少地与其他实体之间发生相互作用

    • 依赖倒置原则:针对接口编程,依赖于抽象而不依赖于具体

    • 里氏替换原则:俗称LSP, 任何基类可以出现的地方,子类一定可以出现,对实现抽象化的具体步骤的规范

# 缺点:

增加一个产品,需要实现对应的具体工厂类和具体产品类;每个产品需要有对应的具体工厂和具体产品类。

上次更新: 5 个月前