附链
你也可以在这些平台阅读本文:
定义
一个工厂方法,根据传入的不同参数,生成对应的具体实例化对象。
简单工厂属于创建性模式,但是其不属于GOF23种设计模式
场景示例
假设笔者拥有很多品牌的手机(虽然笔者没有),这里以笔者选择品牌手机打电话作为示例。
创建基类(可以是接口或者是抽象类)
创建基类,基类可以是接口或者是抽象类。这里以创建手机接口为例。
/**
* @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 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:11
*/
public class Test {
public static void main(String[] args) {
Phone phone = PhoneFactory.getPhone("iphone");
if (phone != null) {
phone.call();
} else {
System.out.println("无此手机品牌类型");
}
}
}
测试类的输出结果如下:
使用苹果手机拨打电话
类结构图
以上示例类的结构图如下所示
存在的问题
上述模式的实例其实还是可以继续扩展的,例如我们需要额外增加华为手机类型:
我们只需创建一个新的实现类 HuaweiPhone
,同时实现 Phone
接口,然后再在工厂类获取手机方法中增加一个关于创建 HuaweiPhone
实例对象的判断分支。
代码如下:
else if ("huaweiphone".equalsIgnoreCase(type)) {
return new HuaweiPhone();
}
使用简单工厂模式,我们会发现一个问题: 增加如上的判断分支修改了我们原有的工厂类方法。
即不满足于开闭原则中所规定的:
软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的
这个问题在工厂方法模式中得到了很好的解决。
方案改进
除了工厂方法外,我们还可以利用反射来弥补简单工厂的扩展性。
工厂类改进
/**
* @author zhh
* @description 手机工厂类
* @date 2020-02-10 11:09
*/
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 2020-02-10 11:11
*/
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()
方法为例)
总结
适用场景
- 工厂类负责创建的对象比较少
- 客户端只关注传入工厂类的参数,不关心实体类对象创建的具体过程
优点
- 客户端不用去关心实体类对象创建的具体过程,从而达到了解耦的效果
- 工厂类可以根据客户端输入的条件动态地去实例化对应的类
缺点
工厂类的职责相对过重,新增产品需要修改工厂类的判断逻辑,违背了开闭原则
参考
- 《Head First 设计模式》
- 《大话设计模式》