创建者模式-工厂模式
# 概念
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
工厂模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
# 使用场景
- jdbc 连接数据库,硬件访问,降低对象的产生和销毁。
# 结构
简单工厂模式: 一个模块仅需要一个工厂类,没有必要把它产生出来,使用静态的方法。
多个工厂类: 每个人种(具体的产品类)都对应了一个创建者,每个创建者独立负责创建对应的产品对象,非常符合单一职责原则。
代替单例模式: 单例模式的核心要求就是在内存中只有一个对象,通过工厂方法模式也可以只在内存中生产一个对象。
延迟初始化: ProductFactory 负责产品类对象的创建工作,并且通过 prMap 变量产生一个缓存,对需要再次被重用的对象保留。
# 简单工厂模式
简单工厂模式不属于 23 种设计模式,它是一种编码风格,他有点类似于策略模式,根据不同的参数返回不同的实现。
public class SimpleCoffeeFactory {
public Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffee;
}
}
# 小结
简单工程虽然不属于 23 种设计模式,但也很常见,它主要的思想是通过一个工厂(Factory)类来管理产品,对调用者隐藏了产品构造的细节,只管用(接口方法)就完事了。
优点
- 解除耦合,降低客户端修改的可能性,更加容易扩展。
缺点
- 违背了开闭原则,之后有新的产品需要改写 Factory 类代码
场景
在实际开发中,遇到需要获取多个不同型号设备的设备信息,而不同型号调用的 api 接口不一样,就会出现以下情况:
deviceList.forEach(device -> {
DeviceInfo info = null;
if("001".equals(device.getModel())){
info = deviceAService.getInfo(device);
} else if("002".equals(device.getModel()){
info = deviceBService.getInfo(device);
}
...
})
使用简单工厂
DeviceInfoFactory deviceInfoFactory = new DeviceInfoFactory();
deviceList.forEach(device -> {
DeviceInfo info = deviceInfoFactory.getDeviceInfo(device);
})
看似简洁了,不过是把上面的代码写去了 Factory 类,因此只能说这是一种编码风格,并没有设计可言。
# 源码应用
- JDK:java.util.Calendar
- JDK:java.text.NumberFormat
- JDK:java.util.ResourceBundle
# 扩展:简单工厂模式 + 配置文件(解除耦合)
可以通过工厂模式 + 配置文件的方式解除工厂对象和产品对象的耦合。在工厂类中加载配置文件中的全类名,并创建对象进行存储,客户端如果需要对象,直接进行获取即可。
定义配置文件
american=com.itheima.pattern.factory.config_factory.AmericanCoffee
latte=com.itheima.pattern.factory.config_factory.LatteCoffee
改造工厂类
public class CoffeeFactory {
private static Map<String,Coffee> map = new HashMap();
static {
Properties p = new Properties();
InputStream is = CoffeeFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
p.load(is);
//遍历Properties集合对象
Set<Object> keys = p.keySet();
for (Object key : keys) {
//根据键获取值(全类名)
String className = p.getProperty((String) key);
//获取字节码对象
Class clazz = Class.forName(className);
Coffee obj = (Coffee) clazz.newInstance();
map.put((String)key,obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static Coffee createCoffee(String name) {
return map.get(name);
}
}
# 静态(简单)工厂模式
这跟简单工厂模式一样,不属于 23 种设计模式,只是把简单工厂定义为静态的。
public class SimpleCoffeeFactory {
public static Coffee createCoffee(String type) {
Coffee coffee = null;
if("americano".equals(type)) {
coffee = new AmericanoCoffee();
} else if("latte".equals(type)) {
coffee = new LatteCoffee();
}
return coffe;
}
}
# 工厂方法模式
# 代码
抽象工厂
public interface CoffeeFactory {
Coffee createCoffee();
}
具体工厂
public class LatteCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new LatteCoffee();
}
}
public class AmericanCoffeeFactory implements CoffeeFactory {
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
咖啡店类
public class CoffeeStore {
private CoffeeFactory factory;
public CoffeeStore(CoffeeFactory factory) {
this.factory = factory;
}
public Coffee orderCoffee(String type) {
Coffee coffee = factory.createCoffee();
coffee.addMilk();
coffee.addsugar();
return coffee;
}
}
# 小结
工厂方法模式相较于简单工厂多了 抽象工厂
角色,再把原本的工厂角色根据产品拆分出了多个产品工厂,每个产品工厂负责自己的产品生成,将来有新的产品,只需要添加新的产品工厂和产品类即可。
优点: 解决了简单工厂模式的缺点
缺点: 产品越多,类就越多,增加了系统的复杂度
# 源码应用
- java.net.URLStreamHandlerFactory
点击查看
public interface URLStreamHandlerFactory {
/**
* Creates a new {@code URLStreamHandler} instance with the specified
* protocol.
*
* @param protocol the protocol ("{@code ftp}",
* "{@code http}", "{@code nntp}", etc.).
* @return a {@code URLStreamHandler} for the specific protocol, or {@code
* null} if this factory cannot create a handler for the specific
* protocol
* @see java.net.URLStreamHandler
*/
URLStreamHandler createURLStreamHandler(String protocol);
}
- javax.xml.bind.JAXBContext#createMarshaller
- java.util.Collection#iterator
点击查看
- Collection 接口是抽象工厂类
- ArrayList 是具体的工厂类
- Iterator 接口是抽象商品类
- ArrayList 类中的 Iter 内部类是具体的商品类
# 总结
工厂方法模式是我翻阅了最多资料去理解的一个模式,在咖啡店的例子中,咖啡店类需要传入具体的产品工厂,有人会有疑问:
那跟我直接 new 有什么区别,我都知道是什么产品工厂了,还不知道我是什么产品吗?
有这样的疑问(包括我自己)是因为没能把设计模式代入实际开发中,
如果这是一个 Spring 项目,再把 CoffeeStore
看作 Service,那么 CoffeeFactory 接口的具体实现则有 Spring 注入,还真不一定知道是什么产品工厂,在实际开发中,工厂模式会与策略模式配合使用,因此单看工厂模式是比较难理解的。