设计模式系列之 工厂方法模式

定义

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。工厂方法让类的实例化推迟到子类中进行。

该定义是对生产者一方的描述,涉及四种角色,包括接口、接口实现类、被实例化的类、抽象产品(隐含)。其中

  1. 接口是指工厂接口(广义接口,在java中可以是接口或者抽象类),该接口定义一个生产产品的方法;

  2. 接口实现类指工厂接口的实现子类;

  3. 实例化的类指生产的产品;

  4. 抽象产品指产品的父类。工厂接口中要先给出抽象产品,其子类才能实例化具体的产品;

定义中第二句话实际上给出了工厂方法的功能,即让子类决定要实例化的类是哪一个。此处的“决定”并不是指运行时的决定,而是在编码过程中程序员的决定。工厂子类和产品实例的对应关系在编码时已经确定,执行时选择哪个工厂实例就可以生产对应的产品。举个例子,工厂子类和产品实例的关系好比是火车和目的地的关系,选择了开往北京的火车,那么最终的目的地就是北京,不会是其他地方。

注意,该模式的核心是工厂方法,实际上这个方法是一个抽象方法,用于定义创建产品的规范。

模式结构

​ 从定义可知,除客户端外,该模式包含了四种角色。从两个平行的类层级出发,可对这四种角色分类。

  1. 相关簇
    • 工厂接口和工厂接口实现类属于工厂相关簇,两者之间存在“高低层”的关系
    • 抽象产品和产品实例都属于产品相关簇,两者之间存在“高低层”的关系
  2. 等级
    • 工厂接口和抽象产品属于一个等级,属于底层等级,用于定义规范或框架。两者可以相互调用
    • 工厂接口实现类和产品实例属于一个等级,属于高层等级,用于实现框架或规范。两者可以相互调用

四种角色对应关系的表格如下,列表示产品簇,行表示同等级

表1 两种平行的类层级
相关簇相关簇
同等级抽象产品抽象工厂
同等级实例产品工厂实现类

四种角色对应UML图如下,列表示产品簇,行表示同等级

图一 工厂方法模式UML图

设计模式套路:抽象之间建立依赖关系,形成框架或规范;实例与抽象之间、实例与实例之间中进行实际的交互,实现OO的多态。

代码实践

步骤1 创建抽象产品。产品作为被操作对象,要先于同等级的工厂创建。

public abstract class AbstractProduct {
    /**
     * 产品抽象方法
     */
    abstract  void productAction();
    
    /**
     * 产品其他公有行为
     */
    public void powerOn(){
        System.out.println("打开电源");
    }

    /**
     * 产品其他公有行为
     */
    public void lightOn(){
        System.out.println("打开灯光");
    }

    /**
     * 产品其他公有行为
     */
    public void dance(){
        System.out.println("开始跳舞");
    }
}

步骤2 创建工厂接口

public abstract class AbstractFactoryMethod {
    /**
     * 工厂方法,也是模式命名的来由
     * @return
     */
    abstract AbstractProduct createProduct();

    /**
     * 方法中用到了工厂方法
     * 具体工厂方法生产什么产品根据当前类的子类决定
     */
    public void productShow(){
       /* 依赖倒置。从依赖具体product改为依赖抽象AbstractProduct
        父类中用到了工厂方法,而工厂方法的实现是在子类中。*/
        AbstractProduct  product= createProduct();
        product.powerOn();
        product.lightOn();
        product.dance();
    }
}

注意,工厂接口中的工厂方法可以在任一地方使用,包括工厂接口内部。

步骤3 创建产品实例

/**
*小马
*/
public class Horse extends AbstractProduct {
    @Override
    void productAction() {
        System.out.println("小马在奔跑");
    }
}

/**
*小牛
*/
public class cow extends AbstractProduct {
    @Override
    void productAction() {
        System.out.println("小牛在吃草");
    }
}

步骤4 创建工厂实现类

/**
*小马工厂
*/
public class HorseFactory extends AbstractFactoryMethod {
    @Override
    AbstractProduct createProduct() {
        return new Horse();
    }
}

/**
*小牛工厂
*/
public class CowFacory extends AbstractFactoryMethod {
    @Override
    AbstractProduct createProduct() {
        return new Cow();
    }
}

以上代码的类图如下

图二 不包含依赖关系
图三 包含依赖关系

特点

  • 优点

    • 避免了简单工厂的三个缺点,即符合开闭原则、可以使用继承结构、避免因创建功能集中的工厂出错使系统瘫痪。
    • 通过工厂名即可获取对应产品,不必关系产品的创建过程。
  • 缺点

    • 抽象方法只能生产一种产品
    • 类爆炸问题
    • 提高了系统的抽象性和理解难度
  • 满足的设计原则

    • 满足开闭原则

      在简单工厂模式中,一旦需要生产新产品,都需要修改正在运行的历史代码,这不符合开闭原则。

      在工厂方法模式中,添加新产品时,不需要修改正在运行的历史代码。只需要新产品实现抽象产品,同时添加一个对应的实现抽象工厂的产品工厂

    • 满足依赖倒置原则

      在不使用工厂方法模式的代码结构中,产品消费者依赖产品生产者,即高层依赖低层。

      使用工厂方法模式后,产品消费者依赖抽象产品(业务关注产品提供的服务),具体的产品生产者也依赖抽象产品(抽象产品规定了产品该怎么生产,是规范),此时发生了依赖倒置(产品生产者(低层)依赖抽象产品(高层))

适用场景

同简单工厂模式类似,首先是需要创建产品的场景。特别地,当需要生成的产品不多且不会增加,一个具体工厂类就可以完成任务时,可删除抽象工厂类。这时工厂方法模式将退化到简单工厂模式。

  • 客户只知道产品的工厂名,不知道具体的产品名。譬如TCL电视工厂、小米手机工厂。
  • 客户不关系产品创建过程,需要时再动态指定。可将具体工厂类的类名存储在配置文件或数据库中。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值