👉文章示例代码👈
定义
一个工厂方法,根据传入的不同参数,生成对应的具体实例化对象。
简单工厂属于创建性模式,但是其不属于GOF23种设计模式
场景示例
假设笔者拥有很多品牌的手机(虽然笔者没有),这里以笔者选择品牌手机打电话作为示例。
创建基类(可以是接口或者是抽象类)
创建基类,基类可以是接口或者是抽象类。这里以创建手机接口为例。
1 2 3 4 5 6 7 8 9 10 11 12
|
public interface Phone {
void call(); }
|
创建实现类
创建实现类实现手机接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
public class IPhone implements Phone {
public void call() { System.out.println("使用苹果手机拨打电话"); } }
public class MiPhone implements Phone {
public void call() { System.out.println("使用小米手机拨打电话"); } }
|
创建工厂类
创建手机工厂类,实现获取手机的方法,根据方法的入参生成对应实现类的对象。
使得应用层不依赖对应的具体实现类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class PhoneFactory {
public static Phone getPhone(String type) { if ("iphone".equalsIgnoreCase(type)) { return new IPhone(); } else if ("miphone".equalsIgnoreCase(type)) { return new MiPhone(); } return null; } }
|
测试类及输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
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
实例对象的判断分支。
代码如下:
1 2 3
| else if ("huaweiphone".equalsIgnoreCase(type)) { return new HuaweiPhone(); }
|
使用简单工厂模式,我们会发现一个问题: 增加如上的判断分支修改了我们原有的工厂类方法。
即不满足于开闭原则中所规定的:
软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的
这个问题在工厂方法模式中得到了很好的解决。
方案改进
除了工厂方法外,我们还可以利用反射来弥补简单工厂的扩展性。
工厂类改进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
public class PhoneFactory {
public static Phone getPhone(Class clazz) { try { return (Phone) Class.forName(clazz.getName()).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } }
|
测试类改进
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
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 设计模式》
- 《大话设计模式》