1 工厂方法概述
工厂方法模式(Factory Method Pattern)
将对象的创建延迟到子类中实现,每个具体产品类都有一个对应的工厂类负责创建,从而使得系统更加灵活。客户端可以通过调用工厂方法来创建所需的产品,而不必关心具体的实现细节。这种模式符合开放-封闭原则,对扩展开放、对修改关闭。
2 工厂方法模式 与 简单工厂模式 的区别
不同于 简单工厂模式
定义一个工厂方法来创建产品,工厂方法模式
的 每个产品都有一个对应的工厂类来创建。这样每个产品类和工厂类相互独立,可以独立进行扩展,符合开放-封闭原则。例如,在一个图形绘制软件中,可以使用 工厂方法模式
来创建不同类型的图形对象,如圆形、矩形、三角形等,每个图形都有一个对应的工厂类来创建。
假设我们要设计一个电子产品工厂来生产不同类型的产品,如手机、电视、电脑等。如果使用 简单工厂模式
,我们需要一个工厂类来创建所有不同类型的电子产品,客户端代码需要指定电子产品类型,工厂类根据类型来创建相应的产品对象。但是,随着产品类型的增加,工厂类的代码会越来越复杂,难以维护和扩展。
相反,如果使用 工厂方法模式
,每个产品类型都有一个对应的工厂类来创建,工厂方法
可以独立进行扩展,每个工厂类只需要关心自己负责的产品类型,代码更加清晰和易于维护。例如,手机工厂类负责创建手机产品对象,电视工厂类负责创建电视产品对象,电脑工厂类负责创建电脑产品对象等。客户端代码只需要知道所需的产品类型,选择对应的工厂类即可,不需要知道具体的创建细节。
3 模式详细说明
首先看 工厂方法模式
的类图:
从上面的类图,可以知道简单工厂模式包含下面的角色:
(1) IProduct
: 产品的接口定义。
(2) ConcreteProductX
: IProduct
的具体实现类。
(3) IProductFactory
: 产品工厂接口定义。
(4) ProductXFactory
: IProductFactory
的具体实现类,用于生产对应的 ConcreteProductX
。
(5) ProductFactoryProvider
: 根据传入的工厂类型,返回对应的产品工厂实例。
(6) Client
: 客户类,它不会直接依赖 ConcreteProductX
,而是依赖 IProduct
,它使用 ProductFactory
创建 IProduct
的具体实例。
4 工厂方法模式例子
IProduct接口
interface IProduct {
void operation();
}
ConcreteProductX 产品的具体实现类
/**
* 具体产品A类
*/
class ConcreteProductA implements IProduct {
@Override
public void operation() {
System.out.println("I am product A");
}
}
/**
* 具体产品B类
*/
class ConcreteProductB implements IProduct {
@Override
public void operation() {
System.out.println("I am product B");
}
}
/**
* 具体产品C类
*/
class ConcreteProductC implements IProduct {
@Override
public void operation() {
System.out.println("I am product C");
}
}
IProductFactory 工厂定义接口
/**
* 产品工厂类接口定义
*/
interface IProductFactory {
IProduct createProduct();
}
ProductXFactory 工厂具体实现类
/**
* 生产A产品的工厂类
*/
class ProductAFactory implements IProductFactory {
@Override
public IProduct createProduct() {
return new ConcreteProductA();
}
}
/**
* 生产B产品的工厂类
*/
class ProductBFactory implements IProductFactory {
@Override
public IProduct createProduct() {
return new ConcreteProductB();
}
}
/**
* 生产C产品的工厂类
*/
class ProductCFactory implements IProductFactory {
@Override
public IProduct createProduct() {
return new ConcreteProductC();
}
}
ProductFactoryProvider 产品工厂实例提供者
使用反射技术,根据传入的 工厂全局限定名 生成对应的工厂实例:
/**
* 产品工厂实例提供者
*/
class ProductFactoryProvider {
/**
* 记录 工厂全局限定名 与 工厂实例 的对应关系
* Key:工厂类全局限定名
* value: 工厂实例
*/
private static Map<String, IProductFactory> factoryMap = new HashMap<>();
/**
* 获取工厂实例
* @param factoryClassName 工厂全局限定名
* @return 工厂实例
*/
public synchronized static IProductFactory getProductFactory(String factoryClassName) {
// 如果 factoryMap 包含要获取的工厂类的全局限定名,则直接返回
if (factoryMap.containsKey(factoryClassName)) {
return factoryMap.get(factoryClassName);
}
// 创建工厂实例,并存到 factoryMap 中
try {
IProductFactory productFactory = (IProductFactory) Class.forName(factoryClassName).newInstance();
factoryMap.put(factoryClassName, productFactory);
return productFactory;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
Client 使用简单工厂生成产品的客户类
public class Client {
public static void main(String[] args) throws Exception {
List<IProduct> productList = new ArrayList<>();
productList.add(ProductFactoryProvider
.getProductFactory("com.design.pattern.ProductAFactory")
.createProduct());
productList.add(ProductFactoryProvider
.getProductFactory("com.design.pattern.ProductBFactory")
.createProduct());
productList.add(ProductFactoryProvider
.getProductFactory("com.design.pattern.ProductCFactory")
.createProduct());
for (IProduct product : productList) {
product.operation();
}
}
}
5 工厂方法模式在 slf4j 库中的应用
在日常开发过程中,我们经常会使用 slf4j + log4j 作为平台的日志框架,里面的 LoggerFactory
的设计就是使用了 工厂方法模式
。
其中,slf4j 定义了 org.slf4j.Logger
接口,它有以下实现类:
org.slf4j.helpers.NOPLogger
:NOP
代表“no operation”(无操作)。它的作用是提供一个空的日志记录器实现,即它不会执行任何实际的日志记录操作。org.slf4j.helpers.SubstituteLogger
: 用于替换真实的日志记录器实现,以便在测试环境中进行测试。SubstituteLogger
记录所有的日志记录请求,但不会将它们发送到真实的日志记录器实现,而是存储在内存中。在测试过程中,测试人员可以通过访问SubstituteLogger
的内部状态来检查哪些日志记录请求被发起,以及它们的参数和级别。org.apache.logging.slf4j.Log4jLogger
: Log4j 的 Logger 实现。
每个 Logger 都有一个对应的 Factory 类,这些 Factory 类都是 org.slf4j.ILoggerFactory
的实现类,Logger 与 LoggerFactory 的类图(只列个大概,大家有兴趣可以自己再细看)如下:
6 总结
工厂方法模式
的优点包括:
- 通过将对象的创建过程抽象出来,使得客户端代码可以更加清晰地表达其意图,并且与具体产品的类名解耦,从而提高了代码的灵活性和可维护性。
- 工厂方法模式可以支持多态性,客户端代码可以通过使用抽象工厂接口,以一种统一的方式使用不同的具体工厂和产品,从而减少了代码的重复。
- 工厂方法模式可以支持扩展性,通过添加新的具体工厂和产品,可以方便地扩展应用程序的功能,而无需修改现有的代码。
工厂方法模式
的缺点包括:
- 工厂方法模式需要客户端代码实例化具体的工厂对象,这可能会导致代码的复杂性和耦合性增加。
- 工厂方法模式会增加应用程序的类的数量,因为需要为每个具体产品创建一个具体工厂,这可能会使代码变得复杂。
- 工厂方法模式可能会导致性能问题,因为它需要额外的对象创建和管理,可能会影响应用程序的性能。