简单工厂属于创建性模式,但是其不属于GOF23种设计模式

定义

一个工厂方法,根据传入的不同参数,生成对应的具体实例化对象。

简单工厂模式实例

我这里以选择手机打电话为例子

创建一个基类(可以是接口或者是抽象类)

/**
 * @author zhh
 * @description 手机接口
 * @date 2019-10-12 00:24
 */
public interface Phone {
    /**
     * 拨打电话
     */
    void call();
}

创建实现基类方法的实体类

/**
 * @author zhh
 * @description 苹果手机
 * @date 2019-10-12 00:26
 */
public class IPhone implements Phone {
    public void call() {
        System.out.println("IPhone call...");
    }
}
/**
 * @author zhh
 * @description 小米手机
 * @date 2019-10-12 00:27
 */
public class MiPhone implements Phone {
    public void call() {
        System.out.println("MiPhone call...");
    }
}

创建工厂类,根据入参生成对应的实体类的对象

/**
 * @author zhh
 * @description 手机工厂类
 * @date 2019-10-12 00:29
 */
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 2019-10-12 00:32
 */
public class Test {
    public static void main(String[] args) {
        Phone phone = PhoneFactory.getPhone("iphone");
        if (phone != null) {
            phone.call();
        } else {
            System.out.println("无此手机类型...");
        }
    }
}

输出结果:

IPhone call…

存在的问题

上述模式的实例其实还是可以扩展的,例如我们要额外增加华为手机类型:

我们只需创建一个新的实体类 HuaweiPhone ,同时实现基类 Phone ,然后再在工厂类当中增加一个关于创建 HuaweiPhone 实例对象的判断分支。

else if ("huaweiphone".equalsIgnoreCase(type)) {
    return new HuaweiPhone();
}

使用简单工厂模式,我们会发现一个问题: 增加如上的判断分支修改了我们原有的工厂类方法。

即不满足于开闭原则中所规定的:

软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的

这个问题在工厂方法模式中得到了很好的解决。

实例改进

除了工厂方法外,我们还可以利用反射来弥补简单工厂的扩展性

工厂类改进

/**
 * @author zhh
 * @description 手机工厂类
 * @date 2019-10-12 00:29
 */
public class PhoneFactory {
    /**
     * 获取手机(方案改进, 利用反射)
     * @param clazz 类对象
     * @return
     */
    public static Phone getPhone(Class clazz) {
        try {
            return (Phone) Class.forName(clazz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试类改进

/**
 * @author zhh
 * @description 测试类
 * @date 2019-10-12 00:32
 */
public class Test {
    public static void main(String[] args) {
        Phone phone = PhoneFactory.getPhone(IPhone.class);
        if (phone != null) {
            phone.call();
        } else {
            System.out.println("无此手机类型...");
        }
    }
}

这时候新增华为手机类型 HuaweiPhone ,我们只需要传递 HuaweiPhone 这个类对象即可,而工厂类是不需要变动的,这种改进从一定程度上满足了开闭原则。

源码中的用例

  • java.util.Calendar类(createCalendar方法)
  • Class.forName(“**”)加载数据库驱动
  • Logback(以具体实现ch.qos.logback.classic.LoggerContext.getLogger方法为例)

总结

适用场景

  • 工厂类负责创建的对象比较少
  • 客户端只关注传入工厂类的参数,不关心实体类对象创建的具体过程

优点

  • 客户端不用去关心实体类对象创建的具体过程,从而达到了解耦的效果
  • 工厂类可以根据客户端输入的条件动态地去实例化对应的类

缺点

新增产品需要修改工厂类的判断逻辑,违背了开闭原则