当前位置: 首页 > news >正文

抽象工厂模式 Abastract Factory Pattern

基本内容

定义一个抽象工厂接口来创建一系列相关或依赖对象的家族,而无需指定它们具体的类。其核心思想是将对象的创建与使用解耦,允许客户端通过抽象接口来获取一组相关的对象,而无需关心这些对象的具体实现。

回顾工厂方法模式

在工厂方法模式中,示例如下:

// 产品接口
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.增加了系统的抽象性和理解难度
不过在实际应用中,一方面如果我们确定了产品族内容,则可以考虑,另一方面,产品升级是正常的,不应该有洁癖或者完美主义的情节。只要不频繁升级,也可以根据实际情况考虑不遵循开闭原则。

http://www.dtcms.com/a/304357.html

相关文章:

  • 从本地 Docker 部署的 Dify 中导出知识库内容(1.6版本亲测有效)
  • 设计一个高可用、可拓展、监控报警系统,使用普罗米修斯和grafana,并给出go实现
  • 无穿戴动作捕捉技术:驱动历史活化、乐园叙事与教育沉浸的文旅利器
  • JVM知识点(2)
  • 从协议栈到ath12k_mac_op_tx的完整调用路径
  • Leetcode——41. 缺失的第一个正数
  • 前端学习日记(十五)
  • 深入理解图像插值:从原理到应用
  • 答题抽奖活动小程序技术复盘
  • unittest错误重跑与测试用例跳过机制
  • 操作系统-lecture2(操作系统结构)
  • Unity的GameObject.Instantiate的使用
  • 津发科技带你了解皮肤电信号中的SCL与SCR
  • SuperClaude Framework 使用指南
  • Ubuntu20.04子系统
  • RPG增容2.尝试使用MMC根据游戏难度自定义更改怪物的属性(二)
  • 基于STM32的PD抓包器
  • Vue3 状态管理新选择:Pinia 从入门到实战
  • Item24:若所有参数皆需类型转换,请为此采用non-member函数
  • [leetcode] 组合总和
  • 《林景媚与数据库神谕》
  • 【C++算法】82.BFS解决FloodFill算法_被围绕的区域
  • 驱动(platform)
  • 青少年软件编程图形化Scratch等级考试试卷(三级)2025年6月
  • CentOS Nginx 1.13.9 部署文档
  • Elasticsearch索引设计与性能优化实战指南
  • 使用Y modem协议进行瑞萨RX MCU OTA数据传输
  • vim的`:q!` 与 `ZQ` 笔记250729
  • 数据结构之时间复杂度
  • 【绘制图像轮廓】——图像预处理(OpenCV)