五、工厂方法模式


1 基本介绍

工厂方法模式(Factory Method Pattern)是一种 创建型 设计模式,它 定义了一个创建对象的接口,但 让子类决定要实例化的类是哪一个。它不同于 简单工厂模式,工厂方法模式将对象的创建逻辑分散到各个具体的工厂类中,而不是集中在一个工厂类中。

2 案例

本模式的案例 和 简单工厂模式的案例很相似,只不过将其中的单个工厂改为了 一个抽象工厂接口 + 多个实现类。

本案例中的类如下所示:

  • 饮品:Drink 抽象类有一个抽象方法 drink(),代表喝这个饮品。它有两个子类:Tea 类和 Coffee 类。
  • 工厂:DrinkFactory 接口有一个方法,能够根据饮品的类型生产对应的饮品。它有两个实现类,分别为 TeaFactory 类和 CoffeeFactory 类。
  • 客户端:Client 类中使用了不同的饮品工厂,创建了不同类型的饮品。

2.1 Drink 抽象类

public abstract class Drink { // 饮品的抽象类
    public abstract void drink(); // 喝饮品

    protected String name; // 饮品的名称

    public Drink(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

2.2 Tea 类

public class Tea extends Drink { // 茶饮品
    public Tea(String name) {
        super(name);
    }

    @Override
    public void drink() {
        System.out.println("你喝了[" + super.name + "]这个茶饮品");
    }
}

2.3 Coffee 类

public class Coffee extends Drink { // 咖啡饮品
    public Coffee(String name) {
        super(name);
    }

    @Override
    public void drink() {
        System.out.println("你喝了[" + super.name + "]这个咖啡饮品");
    }
}

2.4 DrinkFactory 类

public interface DrinkFactory  { // 饮品工厂接口
    Drink createDrink(String drinkName); // 创建 指定名称 的饮品
}

2.5 TeaFactory 类

public class TeaFactory implements DrinkFactory { // 茶饮品工厂类
    @Override
    public Drink createDrink(String drinkName) {
        return new Tea(drinkName); // 创建 茶饮品 的对象
    }
}

2.6 CoffeeFactory 类

public class CoffeeFactory implements DrinkFactory { // 咖啡饮品工厂类
    @Override
    public Drink createDrink(String drinkName) {
        return new Coffee(drinkName); // 创建 咖啡饮品 的对象
    }
}

2.7 Client 类

public class Client { // 使用 具体饮品工厂创建具体饮品 的客户端
    public static void main(String[] args) {
        DrinkFactory teaFactory = new TeaFactory();
        Drink tea = teaFactory.createDrink("铁观音");
        tea.drink();

        DrinkFactory coffeeFactory = new CoffeeFactory();
        Drink coffee = coffeeFactory.createDrink("拿铁");
        coffee.drink();
    }
}

2.8 Client 类运行结果

你喝了[铁观音]这个茶饮品
你喝了[拿铁]这个咖啡饮品

2.9 总结

在本模式的案例中,工厂类比 简单工厂模式 多;在构建工厂时,需要构建具体工厂的对象;在生产产品时,只需要传入与对象有关的参数,不需要传入对象类型有关的信息了。

如果想要新增一种具体产品,则只需写 具体产品类其对应的具体工厂类 的代码,而不需要修改 抽象工厂类,这样就 遵守了开闭原则提高了系统的扩展性

此外,本模式还有简单工厂模式的优点——降低系统之间的耦合度,这是因为在本模式中,客户端也不需要知道具体产品的创建逻辑,只要会使用工厂即可。

3 各角色之间的关系

3.1 角色

3.1.1 Product ( 抽象产品 )

该角色负责 定义 所有具体产品的 共性(包括 属性 和 方法),是所有具体产品的 父类 或 被实现的接口。在本案例中,Drink 抽象类扮演了这个角色。

3.1.2 ConcreteProduct ( 具体产品 )

该角色负责 实现 抽象产品所定义的 方法。在本案例中,Tea 类和 Coffee 类都在扮演这个角色。

3.1.3 Factory ( 抽象工厂 )

该角色负责 定义 创建产品对象的 接口。在本案例中,DrinkFactory 接口扮演了这个角色。

3.1.4 ConcreteFactory ( 具体工厂 )

该角色负责 实现 创建产品对象的 接口,而且只负责创建某一种具体产品。在本案例中,TeaFactory 类和 CoffeeFactory 类都在扮演这个角色。

3.1.5 Client ( 客户端 )

该角色负责 使用 具体工厂创建具体产品。在本案例中,Client 类扮演了这个角色。

3.2 类图

alt text
说明:ConcreteProduct 类和 ConcreteFactory 类是一一对应的,它们两个的关系就和 Product 抽象类与 Factory 接口的关系一致。

4 注意事项

  • 考虑系统复杂度:使用工厂方法模式需要引入一个或多个工厂类,这可能会增加系统的复杂度,在决定是否使用此模式时,需要权衡其带来的好处和增加的复杂度。
  • 共同基类或接口:所有产品类必须有一个共同的基类或接口,以便工厂方法能够返回一个 通用 的产品类型,这有助于保持系统的灵活性和可扩展性。
  • 对象重用:在某些情况下,为了 提高性能减少资源消耗,工厂方法可能会返回 缓存的对象对象池中的对象,而不是每次都创建新对象,这需要根据具体的应用场景和需求来决定。
  • 扩展具体产品:当需要新增产品时,只需新增产品类和相应的工厂类。
  • 考虑使用接口:在可能的情况下,将抽象产品角色定义为 接口 而不是 抽象类,可以提高系统的灵活性和扩展性。在 Java 中,一个类只能继承一个父类,但能实现多个接口。

5 在源码中的使用

在 Spring 框架中,工厂方法模式被广泛应用于对象的创建和管理过程中。Spring 的 BeanFactory 接口是工厂方法模式的典型应用,它 定义 了多个 getBean() 方法,允许客户端通过传入不同的条件(如 Bean 的名称或类型)来获取对应的 Bean 实例。

// 在 org.springframework.beans.factory.BeanFactory 接口中有如下三个方法
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;

这三个方法都没有被实现,只是被定义了,实现交给 BeanFactory 接口的子类,比如AbstractBeanFactory 抽象类。

6 优缺点

优点

  • 扩展性好:当需要新增产品时,只需新增产品类和相应的工厂类,无需修改原有的工厂接口和工厂类,满足开闭原则。
  • 职责单一:每个工厂类只负责创建对应的产品,满足单一职责原则。
  • 降低耦合度:客户端通过工厂接口与具体产品解耦,不需要知道产品的具体实现细节,只需要知道如何获取所需的产品。
  • 灵活性高:可以根据不同的应用场景或条件选择不同的工厂子类来创建产品,增加了系统的灵活性。

缺点

  • 增加了系统的复杂度:每增加一种产品,就需要增加一个对应的产品类和一个对应的工厂子类,这会增加系统中类的数量,从而增加系统的复杂度。
  • 增加了系统的抽象层次:工厂方法模式引入了工厂接口和工厂类,增加了系统的抽象层次,使得系统的理解和维护变得 相对 复杂。
  • 过度设计:在某些情况下,如果系统中产品的 种类不多,或者产品之间的 差异性不大,使用工厂方法模式可能会导致过度设计,增加了不必要的复杂性。这时使用 简单工厂模式 会好一点。
  • 依赖性问题:如果工厂类依赖于其他资源或配置,那么这些依赖项的管理和配置可能会变得复杂。特别是在大型系统中,工厂类的依赖关系可能会形成一个复杂的网络,增加了系统的维护难度。

7 适用场景

  • 多种产品族的创建:当系统中存在多个产品族,并且这些产品族之间存在公共的接口或抽象类,但每个产品族的具体实现不同时,可以使用工厂方法模式。每个产品族对应一个工厂类,负责创建该族中的具体产品对象。
  • 系统需要扩展但不想修改已有代码:当系统需要添加新的产品类型时,如果希望在不修改已有代码的基础上实现扩展,工厂方法模式是一个很好的选择。
  • 复杂的对象创建:当对象的创建过程比较复杂,包含多个步骤或者需要依赖其他对象时,使用工厂方法模式可以将对象的创建过程封装在工厂类中,使得客户端代码更加简洁和易于理解。
  • 隐藏具体产品的创建细节:在某些情况下,我们可能不希望客户端代码知道具体产品的创建细节,或者希望 隐藏 这些细节以简化客户端的使用。工厂方法模式可以通过提供一个共同的接口来隐藏具体产品的创建细节,使得客户端只需通过接口来创建对象。
  • 使用第三方类库:当系统需要使用第三方类库来创建对象,并且这些对象的创建过程比较复杂或者需要遵循特定的规则时,可以使用工厂方法模式来封装这些创建过程,以便在系统中更加方便地使用这些对象。
  • 设计考虑:在系统设计初期,如果预见到将来可能需要添加新的产品类型或者对产品类型进行扩展,可以考虑使用工厂方法模式来为未来可能的扩展 预留接口。这样可以使得系统在扩展时更加灵活和方便。

8 总结

工厂方法模式是一种 创建型 设计模式,它通过多个具体工厂类来创建具体产品,这些具体产品通常具有共同的抽象产品作为父类或接口,这些具体工厂类也有共同的抽象工厂作为接口。

工厂方法模式将具体产品的创建逻辑 封装 在各个工厂类中,客户端无需知道具体产品的类名,只需要知道相应的参数即可,从而 降低了 客户端与具体产品类之间的 耦合度

工厂方法模式对具体产品的种类不做限制,并且具体产品的创建逻辑也可以很复杂。

  • 14
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值