前言:上学期学了一门课——《软件设计模式与体系结构》,当时觉得没什么用,我实际开发中也没使用过,但是刚好报了软件设计师考试,其中也有这部分知识,同时也为了期末考,也就认真学了这门课,理论学的挺6的,发现还挺有意思,刚好最近在优化我的项目,其中也使用到了一些设计模式,想着再系统地复习一遍,记录一下笔记。
一、工厂方法模式
1.概念
工厂方法模式(Factory Method Pattern)是一种类创建型模式,其核心思想是将对象的实例化延迟到子类。通过定义一个创建对象的接口,但由子类决定具体实例化哪个类。
工厂方法模式可以简单理解为:“让专门的‘工厂’来生产‘产品’,你只需要告诉工厂你要什么,不用管具体怎么做出来”。
打个生活中的比方:
你去咖啡店买咖啡(这就是 “你” 作为客户端),咖啡店(相当于 “抽象工厂”)有不同的制作区域:美式咖啡区(具体工厂 A)、拿铁区(具体工厂 B)。
你不需要自己动手做咖啡(不用关心咖啡的具体制作步骤),只需要告诉店员你要美式还是拿铁,对应的制作区就会给你做好(具体工厂生产具体产品)。
如果后来咖啡店新增了卡布奇诺(新的产品),只需要再开一个卡布奇诺制作区(新的具体工厂),原来的区域和你点单的方式都不用变。
核心就是:把 “创建东西” 的工作交给专门的 “工厂”,你只需要用,不用知道东西是怎么造出来的。而且以后想换品种,直接换个工厂就行,不用改自己的代码。
核心目标:
解耦对象的创建与使用,客户端无需关心具体产品类的实例化细节。
支持扩展,符合开闭原则(新增产品时无需修改原有代码)。
2.工厂方法模式的结构
工厂方法模式包含以下4个角色:
角色 | 职责 | 示例(书写工具案例) |
---|---|---|
抽象产品(Product) | 定义产品的规范,描述通用功能。 | WritingTool (书写工具接口) |
具体产品(ConcreteProduct) | 实现抽象产品的接口,提供具体功能。 | Pen 、BallPen 、BrushPen |
抽象工厂(AbstractFactory) | 声明工厂方法(如createProduct() ),用于创建产品对象。 |
WritingToolFactory |
具体工厂(ConcreteFactory) | 实现抽象工厂的工厂方法,返回具体产品的实例。 | PenFactory 、BallPenFactory |
典型代码结构:
抽象工厂类:
public interface LoggerFactory {
Logger createLogger(); // 工厂方法
}
具体工厂类:
public class FileLoggerFactory implements LoggerFactory {
@Override
public Logger createLogger() {
return new FileLogger(); // 返回具体产品
}
}
3.工厂方法模式的优缺点
优点 | 缺点 |
---|---|
1. 隐藏实例化细节:客户端只依赖抽象接口,无需关心具体类。 | 1. 类数量增加:每新增一个产品需新增一个工厂类,导致系统复杂度上升。 |
2. 符合开闭原则:新增产品时只需扩展子类,无需修改原有代码。 | 2. 理解难度提高:引入了抽象层,增加了系统设计复杂性。 |
3. 灵活扩展:支持动态切换产品(如通过配置文件)。 |
4.适用环境
客户端无需知道具体类名:只需通过工厂接口获取对象(如日志记录器、数据库连接器等)。
需要动态扩展产品:系统未来可能新增产品类型(如新增日志存储方式)。
解耦需求:避免客户端代码与具体类直接耦合,提高代码可维护性。
典型场景:
日志记录器(文件日志、数据库日志)。
跨平台UI组件(不同操作系统下的按钮、窗口)。
支付系统(支付宝、微信支付等不同支付方式)。
5.工厂方法模式的应用实例
说明:工厂方法模式就像不同的咖啡制作工坊,每个工坊专注于制作一种咖啡。
// 抽象产品:咖啡
public interface Coffee {
// 制作咖啡的方法
void make();
}
// 具体产品:拿铁咖啡
public class LatteCoffee implements Coffee {
@Override
public void make() {
System.out.println("制作一杯拿铁咖啡");
}
}
// 具体产品:美式咖啡
public class AmericanCoffee implements Coffee {
@Override
public void make() {
System.out.println("制作一杯美式咖啡");
}
}
// 抽象工厂:咖啡工厂
public interface CoffeeFactory {
// 生产咖啡的方法
Coffee createCoffee();
}
// 具体工厂:拿铁咖啡工厂
public class LatteCoffeeFactory implements CoffeeFactory {
@Override
public Coffee createCoffee() {
return new LatteCoffee();
}
}
// 具体工厂:美式咖啡工厂
public class AmericanCoffeeFactory implements CoffeeFactory {
@Override
public Coffee createCoffee() {
return new AmericanCoffee();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建美式咖啡工厂
CoffeeFactory americanFactory = new AmericanCoffeeFactory();
// 制作美式咖啡
Coffee americanCoffee = americanFactory.createCoffee();
americanCoffee.make(); // 输出:制作一杯美式咖啡
// 创建拿铁咖啡工厂
CoffeeFactory latteFactory = new LatteCoffeeFactory();
// 制作拿铁咖啡
Coffee latteCoffee = latteFactory.createCoffee();
latteCoffee.make(); // 输出:制作一杯拿铁咖啡
}
}
在这个例子中:
- 每个咖啡种类(美式、拿铁)是具体产品
- 每个咖啡工厂只生产一种咖啡
- 客户端只需选择相应的工厂,无需知道咖啡的具体制作过程
- 如果要新增咖啡种类,只需添加新的具体产品和对应的工厂
6.总结
核心思想:延迟实例化到子类,通过抽象接口隔离客户端与具体产品。
关键设计原则:依赖倒置原则(DIP)、开闭原则(OCP)。
适用性:适合需要灵活扩展、解耦的场景,但需权衡类数量增加的代价。
通过工厂方法模式,可以构建高扩展性、低耦合的系统架构,尤其适合框架设计和中间件开发。
二、抽象工厂模式
1.概念
抽象工厂模式(Abstract Factory Pattern)是一种对象创建型模式,用于创建一系列相关或相互依赖的对象(即一个产品族),而无需指定它们的具体类。
抽象工厂模式可以简单理解为:“一个‘超级工厂’,专门生产一系列配套的产品,这些产品天生就适合搭配使用”。
打个生活比方:
就像买电脑时,你可以选 “Intel 套装” 或 “AMD 套装”——
- Intel 套装里的 CPU、主板、显卡都是 Intel 体系的,它们彼此兼容;
- AMD 套装里的 CPU、主板、显卡都是 AMD 体系的,也能完美搭配。
你不用自己逐个挑零件(不用担心买的 CPU 和主板不兼容),只需告诉商家要哪个 “套装系列”,商家就会给你一套能一起用的配件。如果以后出了新的 “ARM 套装”,商家只需新增这个系列的生产能力,你买的方式还是一样的。
核心就是:用一个工厂生产一整套 “互相匹配” 的产品,你要啥系列就选啥工厂,不用操心内部零件是否兼容。
核心目标:
封装一组具有共同主题的独立产品
确保客户端始终使用同一产品族中的对象
支持新增产品族(符合开闭原则),但不支持新增产品类型(违反开闭原则)。
2.抽象工厂模式的结构
抽象工厂模式包含以下4个角色:
角色 | 职责 | 示例(界面皮肤库) |
---|---|---|
抽象产品(AbstractProduct) | 定义一类产品的接口(如按钮、文本框)。 | Button 、TextField |
具体产品(ConcreteProduct) | 实现抽象产品接口,属于同一产品族(如Spring风格按钮、Summer风格文本框)。 | SpringButton 、SummerTextField |
抽象工厂(AbstractFactory) | 声明一组创建产品的方法,每个方法对应一个产品等级结构。 | SkinFactory |
具体工厂(ConcreteFactory) | 实现抽象工厂的方法,生成同一产品族的所有具体产品。 | SpringSkinFactory 、SummerSkinFactory |
典型代码结构:
抽象工厂接口:
public interface SkinFactory {
Button createButton();
TextField createTextField();
ComboBox createComboBox();
}
具体工厂类:
public class SpringSkinFactory implements SkinFactory {
@Override
public Button createButton() { return new SpringButton(); }
@Override
public TextField createTextField() { return new SpringTextField(); }
// ...其他产品
}
3.抽象工厂模式的优缺点
优点 | 缺点 |
---|---|
1. 隔离具体类:客户端仅依赖抽象接口,与具体实现解耦。 | 1. 难以扩展新产品类型:新增产品等级需修改所有工厂类,违反开闭原则。 |
2. 保证产品族一致性:客户端始终使用同一主题的产品。 | 2. 系统复杂度高:需设计大量接口和类。 |
3. 支持新增产品族:扩展新主题(如新增Winter风格)无需修改已有代码。 |
4.适用环境
系统需要一组相关产品:如UI主题(所有组件风格一致)、跨平台套件(Windows/Mac按钮+菜单)。
产品族稳定性高:产品等级结构(如按钮、文本框)不会频繁变更。
隐藏具体实现:客户端只需关注抽象接口,不关心具体产品的创建细节。
典型场景:
跨平台应用:同一功能在不同操作系统下的实现(如Windows/Linux的窗口和按钮)。
游戏风格切换:不同主题的游戏场景(如科幻、中世纪风格的武器和地图)。
5.与工厂方法模式对比
对比维度 | 工厂方法模式 | 抽象工厂模式 |
---|---|---|
核心目标 | 创建单一产品 | 创建一组相关产品(产品族) |
工厂职责 | 一个工厂生产一种产品 | 一个工厂生产一族产品 |
扩展性 | 支持新增产品类型(开闭原则) | 仅支持新增产品族(倾斜性开闭原则) |
- 工厂方法模式聚焦于 “单一产品的灵活创建”,适合产品类型单一但实现多样的场景,扩展简单。
- 抽象工厂模式聚焦于 “相关产品族的整体创建”,适合产品之间有依赖关系的场景,能保证产品兼容性,但扩展新的产品类型较复杂。
6.抽象工厂模式的应用实例
说明:抽象工厂模式就像不同品牌的电脑厂商,每个厂商生产一系列配套的电脑部件。
// 抽象产品:CPU
public interface Cpu {
void run();
}
// 具体产品:Intel CPU
public class IntelCpu implements Cpu {
@Override
public void run() {
System.out.println("Intel CPU 运行中");
}
}
// 具体产品:AMD CPU
public class AmdCpu implements Cpu {
@Override
public void run() {
System.out.println("AMD CPU 运行中");
}
}
// 抽象产品:主板
public interface Mainboard {
void connect();
}
// 具体产品:Intel主板
public class IntelMainboard implements Mainboard {
@Override
public void connect() {
System.out.println("Intel主板 连接组件");
}
}
// 具体产品:AMD主板
public class AmdMainboard implements Mainboard {
@Override
public void connect() {
System.out.println("AMD主板 连接组件");
}
}
// 抽象工厂:电脑配件工厂
public interface ComputerFactory {
Cpu createCpu();
Mainboard createMainboard();
}
// 具体工厂:Intel工厂
public class IntelFactory implements ComputerFactory {
@Override
public Cpu createCpu() {
return new IntelCpu();
}
@Override
public Mainboard createMainboard() {
return new IntelMainboard();
}
}
// 具体工厂:AMD工厂
public class AmdFactory implements ComputerFactory {
@Override
public Cpu createCpu() {
return new AmdCpu();
}
@Override
public Mainboard createMainboard() {
return new AmdMainboard();
}
}
// 客户端
public class ComputerClient {
public static void main(String[] args) {
// 创建Intel工厂,生产Intel系列配件
ComputerFactory intelFactory = new IntelFactory();
Cpu intelCpu = intelFactory.createCpu();
Mainboard intelMainboard = intelFactory.createMainboard();
intelCpu.run(); // 输出:Intel CPU 运行中
intelMainboard.connect(); // 输出:Intel主板 连接组件
// 创建AMD工厂,生产AMD系列配件
ComputerFactory amdFactory = new AmdFactory();
Cpu amdCpu = amdFactory.createCpu();
Mainboard amdMainboard = amdFactory.createMainboard();
amdCpu.run(); // 输出:AMD CPU 运行中
amdMainboard.connect(); // 输出:AMD主板 连接组件
}
}
在这个例子中:
- 每个品牌工厂(Intel、AMD)生产一系列配套产品(CPU 和主板)
- 同一工厂生产的产品保证兼容
- 客户端只需选择品牌工厂,就能获得一套兼容的产品
- 如果要新增品牌,只需添加新的具体工厂和对应的一系列产品
7.总结
核心思想:封装产品族的创建,确保客户端代码与具体产品解耦。
关键设计原则:依赖倒置原则(DIP)、开闭原则(部分支持)。
适用性:适合产品族稳定但需灵活切换主题的场景,但需权衡扩展新产品类型的成本。
通过抽象工厂模式,可以高效管理多组相关对象的创建,尤其适用于需要强一致性和主题化的系统设计。