Golang学习笔记_26------通道
Golang学习笔记_27------单例模式
Golang学习笔记_28------工厂方法模式
文章目录
-
- 抽象工厂模式
-
- 一、抽象工厂模式核心概念
-
- [1. 解决的问题](#1. 解决的问题)
- [2. 关键角色](#2. 关键角色)
- [3. 类图](#3. 类图)
- 二、模式特点
- 三、适用场景
-
- [1. 需要创建一组相关或依赖的对象](#1. 需要创建一组相关或依赖的对象)
- [2. 系统需要独立于产品的创建、组合和表示](#2. 系统需要独立于产品的创建、组合和表示)
- [3. 需要确保产品之间的兼容性](#3. 需要确保产品之间的兼容性)
- [4. 需要动态切换产品族](#4. 需要动态切换产品族)
- [5. 需要隐藏具体产品的实现细节](#5. 需要隐藏具体产品的实现细节)
- [四、Go 语言代码示例](#四、Go 语言代码示例)
- 五、与其他模式对比
抽象工厂模式
抽象工厂模式(Abstract Factory)是一种创建型设计模式,用于创建一系列相关或依赖对象的家族,而无需指定它们的具体类。
一、抽象工厂模式核心概念
1. 解决的问题
- 当需要创建多个相互关联或依赖的对象(例如同一主题的 UI 按钮、文本框、下拉框),且希望系统独立于这些对象的创建方式时。
- 避免客户端代码与具体类耦合,支持产品族的切换(例如从 Windows 风格切换到 Mac 风格)。
2. 关键角色
- Abstract Factory(抽象工厂) :声明创建产品对象的方法(如
CreateButton()
,CreateTextBox()
)。 - Concrete Factory(具体工厂) :实现抽象工厂接口,生成具体产品(如
WindowsFactory
,MacFactory
)。 - Abstract Product(抽象产品) :定义产品的接口(如
Button
,TextBox
)。 - Concrete Product(具体产品) :实现抽象产品接口(如
WindowsButton
,MacButton
)。
3. 类图
![](https://i-blog.csdnimg.cn/direct/1f8911ce6f0c4bce9e44875e5f6e1f92.png)
二、模式特点
优点
- 产品族一致性:确保创建的对象属于同一家族(例如不会混用 Windows 按钮和 Mac 文本框)。
- 解耦客户端与具体类:客户端仅依赖抽象接口,与具体实现无关。
- 易于切换产品族 :只需更换具体工厂(如
WindowsFactory
→MacFactory
)。
缺点
- 扩展新产品困难 :若新增产品类型(如
Checkbox
),需修改所有工厂接口。 - 代码复杂度增加:需要定义大量接口和类。
三、适用场景
1. 需要创建一组相关或依赖的对象
抽象工厂模式的核心是创建一组相互关联或依赖的对象,这些对象通常属于同一个产品族。以下是一些典型场景:
示例 1:跨平台 UI 组件库
- 场景:开发一个支持多平台(如 Windows、Mac、Linux)的 UI 组件库,每个平台有自己风格的按钮、文本框、下拉框等组件。
- 问题:如果直接硬编码创建组件,会导致代码与具体平台耦合,难以扩展和维护。
- 解决方案 :使用抽象工厂模式,为每个平台创建一个工厂(如
WindowsFactory
、MacFactory
),每个工厂负责创建该平台的所有组件。
示例 2:游戏中的角色装备
- 场景:游戏中不同种族(如人类、精灵、兽人)的角色需要穿戴不同风格的装备(如武器、盔甲、鞋子)。
- 问题:如果直接创建装备对象,可能会导致人类角色穿戴精灵风格的装备,破坏游戏一致性。
- 解决方案 :使用抽象工厂模式,为每个种族创建一个工厂(如
HumanFactory
、ElfFactory
),确保创建的装备风格一致。
2. 系统需要独立于产品的创建、组合和表示
抽象工厂模式将产品的创建过程封装在工厂中,客户端代码只需要依赖抽象接口,而不需要关心具体实现。
示例 3:数据库访问层
- 场景:系统需要支持多种数据库(如 MySQL、PostgreSQL、Oracle),每种数据库有自己的连接、命令、事务等对象。
- 问题:如果直接在业务逻辑中创建数据库对象,会导致代码与具体数据库耦合,难以切换数据库。
- 解决方案 :使用抽象工厂模式,为每种数据库创建一个工厂(如
MySQLFactory
、PostgreSQLFactory
),客户端通过工厂获取数据库对象,无需关心具体实现。
示例 4:主题切换
- 场景:应用程序支持多种主题(如白天模式、夜间模式),每种主题有不同的颜色、字体、图标等。
- 问题:如果直接在代码中硬编码主题样式,会导致代码难以维护和扩展。
- 解决方案 :使用抽象工厂模式,为每个主题创建一个工厂(如
LightThemeFactory
、DarkThemeFactory
),客户端通过工厂获取主题相关的对象。
3. 需要确保产品之间的兼容性
抽象工厂模式确保创建的对象属于同一个产品族,从而保证它们之间的兼容性。
示例 5:操作系统与硬件驱动
- 场景:开发一个操作系统,需要支持不同硬件厂商的驱动程序(如显卡驱动、声卡驱动、网卡驱动)。
- 问题:如果直接创建驱动对象,可能会导致不兼容的驱动组合(如 Intel 显卡驱动与 AMD 声卡驱动)。
- 解决方案 :使用抽象工厂模式,为每个硬件厂商创建一个工厂(如
IntelFactory
、AMDFactory
),确保创建的驱动对象属于同一厂商。
示例 6:电商平台的支付与物流
- 场景:电商平台需要支持多种支付方式(如支付宝、微信支付)和物流方式(如顺丰、京东物流)。
- 问题:如果直接创建支付和物流对象,可能会导致不兼容的组合(如支付宝与京东物流)。
- 解决方案 :使用抽象工厂模式,为每个支付和物流组合创建一个工厂(如
AlipaySFExpressFactory
、WeChatJDLogisticsFactory
),确保支付和物流对象兼容。
4. 需要动态切换产品族
抽象工厂模式支持在运行时动态切换产品族,而无需修改客户端代码。
示例 7:多语言支持
- 场景:应用程序需要支持多语言(如中文、英文、日文),每种语言有不同的界面文本、日期格式、货币符号等。
- 问题:如果直接在代码中硬编码语言相关的对象,会导致代码难以维护和扩展。
- 解决方案 :使用抽象工厂模式,为每种语言创建一个工厂(如
ChineseFactory
、EnglishFactory
),客户端通过工厂获取语言相关的对象,支持动态切换语言。
示例 8:游戏中的场景切换
- 场景:游戏中不同场景(如森林、沙漠、雪地)有不同的背景、音效、角色模型。
- 问题:如果直接在代码中创建场景对象,会导致代码与具体场景耦合,难以扩展。
- 解决方案 :使用抽象工厂模式,为每个场景创建一个工厂(如
ForestFactory
、DesertFactory
),客户端通过工厂获取场景相关的对象,支持动态切换场景。
5. 需要隐藏具体产品的实现细节
抽象工厂模式将具体产品的创建过程封装在工厂中,客户端只需要依赖抽象接口,无需关心具体实现。
示例 9:日志系统
- 场景:系统需要支持多种日志存储方式(如文件日志、数据库日志、云存储日志)。
- 问题:如果直接在代码中创建日志对象,会导致代码与具体存储方式耦合。
- 解决方案 :使用抽象工厂模式,为每种日志存储方式创建一个工厂(如
FileLogFactory
、DatabaseLogFactory
),客户端通过工厂获取日志对象,无需关心具体实现。
示例 10:缓存系统
- 场景:系统需要支持多种缓存存储方式(如内存缓存、Redis 缓存、Memcached 缓存)。
- 问题:如果直接在代码中创建缓存对象,会导致代码与具体存储方式耦合。
- 解决方案 :使用抽象工厂模式,为每种缓存存储方式创建一个工厂(如
MemoryCacheFactory
、RedisCacheFactory
),客户端通过工厂获取缓存对象,无需关心具体实现。
四、Go 语言代码示例
场景描述
假设需要开发一个跨平台的 UI 组件库 ,支持 Windows 和 Mac 两种风格,每个风格包含按钮(Button
)和文本框(TextBox
)。使用抽象工厂模式实现组件创建。
代码实现
go
package abstract_factory_demo
type Phone interface {
Call(number string)
}
type SmartSpeaker interface {
PlayMusic(song string)
}
type HuaweiPhone struct {
}
func (h *HuaweiPhone) Call(number string) {
println("华为手机拨打电话:" + number)
}
type HuaweiSmartSpeaker struct {
}
func (h *HuaweiSmartSpeaker) PlayMusic(song string) {
println("华为智能音箱播放音乐:" + song)
}
type XiaomiPhone struct {
}
func (x *XiaomiPhone) Call(number string) {
println("小米手机拨打电话:" + number)
}
type XiaomiSmartSpeaker struct {
}
func (x *XiaomiSmartSpeaker) PlayMusic(song string) {
println("小米智能音箱播放音乐:" + song)
}
// 抽象工厂接口
type DeviceFactory interface {
CreatePhone() Phone
CreateSmartSpeaker() SmartSpeaker
}
// 华为工厂
type HuaweiFactory struct {
}
func (hf *HuaweiFactory) CreateSmartSpeaker() SmartSpeaker {
return &HuaweiSmartSpeaker{}
}
func (hf *HuaweiFactory) CreatePhone() Phone {
return &HuaweiPhone{}
}
// 小米工厂
type XiaomiFactory struct {
}
func (xf *XiaomiFactory) CreatePhone() Phone {
return &XiaomiPhone{}
}
func (xf *XiaomiFactory) CreateSmartSpeaker() SmartSpeaker {
return &XiaomiSmartSpeaker{}
}
func getFactory(brand string) DeviceFactory {
if brand == "huawei" {
return &HuaweiFactory{}
} else if brand == "xiaomi" {
return &XiaomiFactory{}
}
return nil
}
func test() {
// 华为工厂
huaweiFactory := getFactory("huawei")
huaweiPhone := huaweiFactory.CreatePhone()
huaweiSmartSpeaker := huaweiFactory.CreateSmartSpeaker()
huaweiPhone.Call("123456")
huaweiSmartSpeaker.PlayMusic("夜曲")
// 小米工厂
xiaomiFactory := getFactory("xiaomi")
xiaomiPhone := xiaomiFactory.CreatePhone()
XiaomiSmartSpeaker := xiaomiFactory.CreateSmartSpeaker()
xiaomiPhone.Call("654321")
XiaomiSmartSpeaker.PlayMusic("稻香")
}
输出结果
plaintext
=== RUN Test_test
华为手机拨打电话:123456
华为智能音箱播放音乐:夜曲
小米手机拨打电话:654321
小米智能音箱播放音乐:稻香
--- PASS: Test_test (0.00s)
PASS
五、与其他模式对比
- 工厂方法模式:关注单一产品的创建,而抽象工厂关注产品族的创建。
- 建造者模式:分步骤构建复杂对象,抽象工厂直接返回完整产品族。