👉文章示例代码👈

定义

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类。

工厂方法模式让类的实例化推迟到子类中进行。

场景示例

笔者这里通过简单工厂模式中示例的演变来进行讲解。

/**
 * @author zhh
 * @description 手机工厂类
 * @date 2020-02-10 11:09
 */
public class PhoneFactory {

    /**
     * 获取手机
     * @param type 手机品牌类型
     * @return
     */
    public static Phone getPhone(String type) {
        if ("iphone".equalsIgnoreCase(type)) {
            return new IPhone();
        } else if ("miphone".equalsIgnoreCase(type)) {
            return new MiPhone();
        }
        return null;
    }
}

我们可以看到简单工厂模式中的手机工厂类并没有子类,创建手机品牌对象都在该工厂类中进行。而工厂模式说的是将创建的职能移交给子类。

下面对代码进行改造,让子类来负责具体手机品牌对象的实例化。

创建基类

这里仍与简单工厂模式中保持一致。

/**
 * @author zhh
 * @description 手机接口
 * @date 2020-02-10 11:04
 */
public interface Phone {

    /**
     * 拨打电话
     */
    void call();
}

创建实现类

这里仍与简单工厂模式中保持一致。

/**
 * @author zhh
 * @description 苹果手机
 * @date 2020-02-10 11:07
 */
public class IPhone implements Phone {

    public void call() {
        System.out.println("使用苹果手机拨打电话");
    }
}

/**
 * @author zhh
 * @description 小米手机
 * @date 2020-02-10 11:08
 */
public class MiPhone implements Phone {

    public void call() {
        System.out.println("使用小米手机拨打电话");
    }
}

创建抽象工厂接口

这里对应做了调整,此时获取手机该方法也不需要入参,具体调用哪个实现全部交由客户端选择。

/**
 * @author zhh
 * @description 手机工厂接口
 * @date 2020-02-10 11:09
 */
public interface PhoneFactory {

    /**
     * 获取手机
     */
    Phone getPhone();
}

创建工厂实现类

创建具体实现类实例化的工厂类,同时实现抽象工厂接口。

/**
 * @author zhh
 * @description 苹果手机工厂类
 * @date 2020-02-10 15:12
 */
public class IPhoneFactory implements PhoneFactory {

    public Phone getPhone() {
        return new IPhone();
    }
}

/**
 * @author zhh
 * @description 小米手机工厂类
 * @date 2020-02-10 15:12
 */
public class MiPhoneFactory implements PhoneFactory {

    public Phone getPhone() {
        return new MiPhone();
    }
}

测试类及输出

/**
 * @author zhh
 * @description 测试类
 * @date 2020-02-10 11:11
 */
public class Test {

    public static void main(String[] args) {
        PhoneFactory phoneFactory = new IPhoneFactory();
        Phone phone = phoneFactory.getPhone();
        phone.call();
    }
}

测试类的输出结果如下:

使用苹果手机拨打电话

类结构图

以上示例类的结构图如下所示

image.png

这里 Phone 有两个子类 IPhone 和 MiPhone ,它们处于同一个产品等级。

IPhoneFactory 工厂只创建 IPhone , MiPhoneFactory 工厂只创建 MiPhone 。

扩展说明

通过工厂方法模式的场景示例我们可以发现,当需要新增加产品时,我们只需要增加一个对应产品的实体类和一个创建对应产品实体类的工厂即可。

举个例子,比如在上述的示例中增加华为手机。

我们只需创建一个新的产品实体类 HuaweiPhone 来实现 IPhone 接口,同时再新建一个创建对应产品实体类的工厂 HuaweiPhoneFactory 来实现 PhoneFactory 接口即可。

这样就不用再对原本的工厂类进行修改,很好的符合了开闭原则。

抽象角色

我们可以从上述示例中抽象出四种角色(忽略客户端):

  • 产品( Phone )
  • 具体的产品( IPhone 、 MiPhone )
  • 创建者( PhoneFactory )
  • 具体的创建者( IPhoneFactory 、 MiPhoneFactory )

源码中的用例

  • java.util.Collection 类中的 iterator() 方法
  • Logback 中的 org.slf4j.ILoggerFactory

总结

适用场景

  • 创建对象需要大量重复的代码
  • 客户端不依赖于产品类实例如何被创建、实现等细节
  • 一个类通过其子类来指定创建哪个对象

优点

  • 用户只需要关心所需产品对应的工厂,无须关心创建细节
  • 加入新产品是符合开闭原则的,提高可扩展性

缺点

  • 类的个数容易过多,增加复杂度
  • 增加了系统的抽象性和理解难度

参考