基本内容
定义一个抽象工厂接口来创建一系列相关或依赖对象的家族,而无需指定它们具体的类。其核心思想是将对象的创建与使用解耦,允许客户端通过抽象接口来获取一组相关的对象,而无需关心这些对象的具体实现。
回顾工厂方法模式
在工厂方法模式中,示例如下:
// 产品接口
public interface Logger {
void log(String message);
}
// 产品实现
public class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println("控制台日志: " + message);
}
}
public class FileLogger implements Logger {
public void log(String message) {
System.out.println("文件日志: " + message);
}
}
// 抽象工厂
public interface LoggerFactory {
Logger createLogger();
}
// 具体工厂
public class ConsoleLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new ConsoleLogger();
}
}
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new FileLogger();
}
}
// 使用
LoggerFactory factory = new ConsoleLoggerFactory();
Logger logger = factory.createLogger();
logger.log("Hello");
这里定义了一个抽象接口,基于该抽象接口实现了一系列具体类,那么这些具体类可以看作是同一类产品。在抽象工厂中,我们也实现了一个对象构造方法,返回的是一类产品,然后我们使用具体工厂实现了抽象工厂的创建方法,每个工厂创建其对应实现类的对象。
总结下:工厂方法模式每个工厂只创建一种产品,而产品归根到底是同一类的,工厂创建产品的方法也是同一种方法的重写。
下面我们考虑:如果有多种相关类别的产品,由此我们引入产品族:
产品族
产品族是指由同一工厂创建的一组产品,这些产品在设计上相互配合,共同完成某个功能或满足某个场景的需求。
我们使用常见的家用电器举例:
上图中的正方形代表洗衣机,圆形代表空调,菱形代表热水器。我们用一个矩形将他们框起来,构成一个产品族,假设这个产品族是美的生产的。这个美的就是一个具体工厂,生产了一个产品族的产品。
我们还知道,除了美的,当然还有其他厂家,比如小米,格力,海信。于是左侧的工厂生产他对应的那一行产品族,即每个具体工厂都生产其对应的洗衣机,空调,热水器。这些产品共同构建一个现代化家庭的常用电器。
示例
回到logger示例中,现在我们有一个产品族,其构成是:logger产品和loggerFormat产品,一个是写入日志的,一个是定义日志格式的。他们配置实现用什么格式写入日志
// 抽象产品1:日志记录器
public interface Logger {
void log(String message);
}
// 抽象产品2:日志格式化器
public interface LoggerFormatter {
String format(String message);
}
// 简洁风格的Logger
public class SimpleLogger implements Logger {
public void log(String message) {
System.out.println("简洁日志: " + message);
}
}
// 简洁风格的Formatter
public class SimpleFormatter implements LoggerFormatter {
public String format(String message) {
return "[" + message + "]";
}
}
// 详细风格的Logger
public class DetailedLogger implements Logger {
public void log(String message) {
System.out.println("详细日志: [时间] " + message);
}
}
// 详细风格的Formatter
public class DetailedFormatter implements LoggerFormatter {
public String format(String message) {
return "详细格式: " + message + " (时间戳)";
}
}
现在我们创建的抽象工厂,显然不是和之前一样定义一个抽象获取对象的方法就行了,而是需要两个
public interface LoggerFactory {
Logger createLogger(); // 创建日志记录器
LoggerFormatter createFormatter(); // 创建日志格式化器
}
然后我们实现具体的工厂,
// 简洁风格工厂
public class SimpleLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new SimpleLogger();
}
public LoggerFormatter createFormatter() {
return new SimpleFormatter();
}
}
// 详细风格工厂
public class DetailedLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new DetailedLogger();
}
public LoggerFormatter createFormatter() {
return new DetailedFormatter();
}
}
客户端使用的时候不需要关注对象创建的过程:
public class Client {
public static void main(String[] args) {
// 使用简洁风格工厂
LoggerFactory simpleFactory = new SimpleLoggerFactory();
Logger simpleLogger = simpleFactory.createLogger();
LoggerFormatter simpleFormatter = simpleFactory.createFormatter();
simpleLogger.log(simpleFormatter.format("Hello"));
// 输出: 简洁日志: [Hello]
// 使用详细风格工厂
LoggerFactory detailedFactory = new DetailedLoggerFactory();
Logger detailedLogger = detailedFactory.createLogger();
LoggerFormatter detailedFormatter = detailedFactory.createFormatter();
detailedLogger.log(detailedFormatter.format("Hello"));
// 输出: 详细日志: [时间] 详细格式: Hello (时间戳)
}
}
总结
抽象工厂模式 = 多个工厂方法 + 产品族约束。
如果你的系统需要同时创建一组相关对象(如UI组件、日志系统、数据库连接池等),抽象工厂模式是更合适的选择。
问题
如果我们继续扩展产品族,例如,在家具里面新增电风扇,那么我们的抽象工厂到具体工厂都需要调整,显然这违背了开闭原则,由此我们知道抽象工厂模式也是有缺点的。即:
1.扩展困难,需要修改很多地方
2.增加了系统的抽象性和理解难度
不过在实际应用中,一方面如果我们确定了产品族内容,则可以考虑,另一方面,产品升级是正常的,不应该有洁癖或者完美主义的情节。只要不频繁升级,也可以根据实际情况考虑不遵循开闭原则。