Golang学习笔记_35——代理模式
Golang学习笔记_36——装饰器模式
Golang学习笔记_37——外观模式
文章目录
享元模式(Flyweight Pattern)详解
一、核心概念
1. 定义
享元模式是一种结构型设计模式,通过共享对象来有效支持大量细粒度对象的复用,从而减少内存占用和提高性能。其核心在于区分:
- 内部状态(Intrinsic):可共享的固定属性(如字符编码、颜色配置)
- 外部状态(Extrinsic):不可共享的运行时上下文(如坐标位置、字体大小)
2. 解决的问题
- 内存资源浪费:大量相似对象导致内存占用过高
- 对象创建开销:高频创建销毁对象影响性能
- 状态分离管理:需要区分对象固定属性与可变属性
3. 核心角色
-
Flyweight(抽象享元)
定义对象接口,声明接收外部状态的方法(如Draw(x,y)
) -
ConcreteFlyweight(具体享元)
实现抽象接口,存储内部状态(如字符编码) -
FlyweightFactory(享元工厂)
创建并管理享元对象池,确保对象复用 -
Client(客户端)
维护外部状态并传递给享元对象
4. 类图
享元模式类图" />
@startuml
class FlyweightFactory {
- pool: map[string]Flyweight
+ GetFlyweight(key): Flyweight
}
interface Flyweight {
+ Operation(extrinsicState)
}
class ConcreteFlyweight {
- intrinsicState: string
+ Operation(extrinsicState)
}
FlyweightFactory o--> Flyweight : manages
Flyweight <|.. ConcreteFlyweight
@enduml
二、特点分析
优点
- 内存优化:减少重复对象的存储开销
- 性能提升:降低对象创建/销毁频率
- 状态解耦:清晰分离内部/外部状态
缺点
- 复杂度增加:需要严格区分内外状态
- 线程安全问题:共享对象需考虑并发访问
- 过度优化风险:不适合对象差异大的场景
三、适用场景
1. 文字编辑器
- 内部状态:字符编码、字体样式
- 外部状态:坐标位置、颜色
- 效果:10万个’A’字符只需1个享元对象
2. 游戏开发
- 内部状态:子弹贴图、伤害值
- 外部状态:发射位置、移动轨迹
- 效果:百万子弹共享10种类型配置
3. 数据库连接池
- 内部状态:连接配置参数
- 外部状态:当前事务状态
- 效果:复用连接避免重复创建
四、代码示例(Go语言)
场景:游戏子弹系统
@startuml
class BulletType {
- name: string
- color: string
- texture: []byte
+ String(): string
}
class BulletFactory {
- pool: map[string]*BulletType
- mu: sync.Mutex
+ GetBulletType(name: string, color: string): *BulletType
}
class Bullet {
- typeInfo: *BulletType
- x: float64
- y: float64
- speed: float64
+ String(): string
}
BulletFactory "1" o-- "many" BulletType : contains > pool
Bullet --> BulletType : typeInfo >
note left of BulletFactory
<<Singleton>>
Use GetBulletFactory() method
to get the single instance
end note
@enduml
package flyweight_demo
import (
"fmt"
"sync"
)
// BulletType 享元对象 子弹类型(内部状态)
type BulletType struct {
name string
color string
texture []byte
}
func (bt *BulletType) String() string {
return fmt.Sprintf("%s-%s", bt.name, bt.color)
}
// 享元工厂
type BulletFactory struct {
pool map[string]*BulletType
mu sync.Mutex
}
var instance *BulletFactory
var once sync.Once
func GetBulletFactory() *BulletFactory {
once.Do(func() {
instance = &BulletFactory{
pool: make(map[string]*BulletType),
}
})
return instance
}
func (bf *BulletFactory) GetBulletType(name, color string) *BulletType {
key := name + "-" + color
bf.mu.Lock()
defer bf.mu.Unlock()
if bt, ok := bf.pool[key]; ok {
return bt
}
newBt := &BulletType{
name: name,
color: color,
texture: make([]byte, 1024*1024),
}
bf.pool[key] = newBt
return newBt
}
// 具体子弹对象(包含外部状态)
type Bullet struct {
typeInfo *BulletType
x, y float64
speed float64
}
func newBullet(name, color string, x, y, speed float64) *Bullet {
factory := GetBulletFactory()
return &Bullet{
typeInfo: factory.GetBulletType(name, color),
x: x,
y: y,
speed: speed,
}
}
func (b *Bullet) String() string {
return fmt.Sprintf("%s @(%.1f,%.1f) speed=%.1f",
b.typeInfo, b.x, b.y, b.speed)
}
func test() {
bullets := make([]*Bullet, 0)
// 创建10000发子弹,实际只生成2种BulletType
for i := 0; i < 5000; i++ {
bullets = append(bullets,
newBullet("AK47", "gold", float64(i), 0, 10),
newBullet("M4A1", "silver", float64(i), 10, 12),
)
}
// 验证对象复用
fmt.Printf("Total bullet types created: %d\n",
len(GetBulletFactory().pool)) // 输出2
fmt.Println(bullets[0].String()) // AK47-gold @(0.0,0.0) speed=10.0
fmt.Println(bullets[1].String()) // M4A1-silver @(0.0,10.0) speed=12.0
}
=== RUN Test_test
Total bullet types created: 2
AK47-gold @(0.0,0.0) speed=10.0
M4A1-silver @(0.0,10.0) speed=12.0
--- PASS: Test_test (0.00s)
PASS
五、高级应用
1. 组合享元模式
type WeaponSkin struct {
baseType *BulletType
sticker string // 扩展装饰属性
}
func (ws *WeaponSkin) GetKey() string {
return ws.baseType.name + "_" + ws.baseType.color + "_" + ws.sticker
}
2. 线程安全优化
// 使用RWMutex提升并发性能
func (bf *BulletFactory) GetBulletTypeSafe(name, color string) *BulletType {
key := name + "_" + color
// 先尝试读锁
bf.mu.RLock()
if bt, exists := bf.pool[key]; exists {
bf.mu.RUnlock()
return bt
}
bf.mu.RUnlock()
// 获取写锁
bf.mu.Lock()
defer bf.mu.Unlock()
// 双检锁防止重复创建
if bt, exists := bf.pool[key]; exists {
return bt
}
newBt := createNewBulletType(name, color)
bf.pool[key] = newBt
return newBt
}
六、与其他模式对比
模式 | 核心目标 | 关键区别 |
---|---|---|
单例模式 | 控制实例数量 | 享元管理多个共享对象 |
对象池模式 | 复用昂贵对象 | 享元侧重状态分离 |
装饰器模式 | 动态扩展功能 | 享元侧重对象复用 |
七、总结
享元模式通过共享细粒度对象,有效解决了:
- 内存占用问题:减少重复对象的存储
- 性能优化问题:降低对象创建开销
- 状态管理问题:明确区分内外状态
在Go语言中实现时需注意:
- 使用
sync.Map
或mutex
保证线程安全 - 严格分离内部/外部状态
- 合理设计对象键值(如颜色+名称组合键)
实际应用场景建议:
- 游戏开发中的粒子系统
- GUI系统的控件复用
- 金融交易中的报价对象池