享元模式
1、定义与核心思想
(1)定义
- 享元模式(Flyweight Pattern)是一种结构型设计模式,其核心在于通过共享技术减少对象数量,降低内存消耗,适用于存在大量相似对象的场景。
- 该模式将对象状态分为内部状态(Intrinsic)和外部状态(Extrinsic),其中内部状态可共享,而外部状态由客户端动态传递。
(2)核心目标
- 内存优化:减少重复对象的创建。
- 性能提升:通过共享对象避免重复计算。
(3)适用场景
- 系统需要创建大量相似对象(如文本编辑器中的字符、游戏中的粒子特效)。
- 对象的大部分状态可外部化,且外部状态与内部状态分离。
(4)应用场景
- 图形渲染系统:共享相同的材质和纹理数据,位置和旋转角度作为外部状态。
- 数据库连接池:复用数据库连接对象,避免频繁创建销毁。
- 游戏开发:子弹、敌人等大量重复对象的实例共享。
- 字符串驻留池:.NET 中的字符串驻留机制是享元模式的典型实现。
2、结构与角色
(1)抽象享元(Flyweight Interface)
public interface IFlyweight
{void Operation(string extrinsicState);
}
(2)具体享元(Concrete Flyweight)
public class ConcreteFlyweight : IFlyweight
{private readonly string _intrinsicState;public ConcreteFlyweight(string intrinsicState){_intrinsicState = intrinsicState;}public void Operation(string extrinsicState){Console.WriteLine($"内部状态: {_intrinsicState}, 外部状态: {extrinsicState}");}
}
(3)享元工厂(Flyweight Factory)
- 管理共享对象池,确保相同内部状态的对象只创建一次。
public class FlyweightFactory
{private readonly Dictionary<string, IFlyweight> _flyweights = new();public IFlyweight GetFlyweight(string key){if (!_flyweights.ContainsKey(key)){_flyweights[key] = new ConcreteFlyweight(key);}return _flyweights[key];}
}
(4)客户端(Client)
3、C# 实现示例
(1)基础案例
- 场景:模拟文本编辑器中的字符渲染,相同字体的字符共享内部状态(字体、颜色),位置由外部状态决定。
public interface ICharacter
{void Draw(int x, int y);
}
public class Character : ICharacter
{private readonly string _font;private readonly string _color;public Character(string font, string color){_font = font;_color = color;}public void Draw(int x, int y){Console.WriteLine($"在位置({x},{y})渲染字体: {_font}, 颜色: {_color}");}
}
public class CharacterFactory
{private Dictionary<string, ICharacter> _characters = new();public ICharacter GetCharacter(string font, string color){string key = $"{font}_{color}";if (!_characters.ContainsKey(key)){_characters[key] = new Character(font, color);}return _characters[key];}
}
class Client
{static void Main(){CharacterFactory factory = new CharacterFactory();ICharacter charA = factory.GetCharacter("Arial", "Red");charA.Draw(10, 20);ICharacter charB = factory.GetCharacter("Arial", "Red"); charB.Draw(30, 40);}
}
在位置(10,20)渲染字体: Arial, 颜色: Red
在位置(30,40)渲染字体: Arial, 颜色: Red
(2)关键优化
- 线程安全:使用
ConcurrentDictionary 管理共享池。
private readonly ConcurrentDictionary<string, Lazy<IFlyweight>> _flyweights = new();
- 外部状态管理:将可变状态(如位置、大小)通过方法参数传递,而非存储于对象内部。
- 组合模式扩展:对于复杂对象,可将享元与非享元对象组合使用。
public class CompositeFlyweight : IFlyweight
{private List<IFlyweight> _children = new();public void Add(IFlyweight flyweight) => _children.Add(flyweight);public void Operation(string extrinsicState){foreach (var child in _children)child.Operation(extrinsicState);}
}
(3)扩展:内存池技术
- 在 C# 中,可通过
ObjectPool 或自定义池实现高效对象复用:
public class ObjectPool<T> where T : new()
{private readonly ConcurrentBag<T> _objects = new();public T GetObject() => _objects.TryTake(out T item) ? item : new T();public void ReturnObject(T item) => _objects.Add(item);
}
(4)最佳实践
- 设计原则:优先识别可共享的内部状态,避免过度设计。
- 性能权衡:在内存敏感场景(如嵌入式系统、移动端)优先使用享元模式。
- 结合其他模式:与工厂模式、组合模式协同解决复杂问题。
4、优缺点分析
(1)优点
- 内存节省:显著减少对象数量(例如,1000个相同字符只需1个享元对象)。
- 性能提升:避免重复初始化开销。
(2)缺点
- 复杂性增加:需明确区分内部与外部状态。
- 线程安全问题:共享对象需考虑并发访问。
(3)对比其他模式
| 模式 | 核心区别 | 适用场景 |
|---|
| 单例模式 | 确保类只有一个实例 | 全局唯一对象(如配置) |
| 工厂模式 | 创建不同类型的对象 | 对象创建逻辑复杂 |
| 装饰模式 | 动态添加职责 | 扩展对象功能 |
代理模式👍
1、定义与核心思想
(1)定义与目的
- 代理模式(Proxy Pattern)是一种结构型设计模式,通过引入代理对象控制对真实对象的访问,核心在于解耦客户端与目标对象,并添加间接层以增强功能(如权限校验、日志记录、延迟加载等)。
(2)结构与角色
- 抽象主题(Subject):定义代理与真实对象的公共接口,确保客户端透明调用。
- 真实主题(Real Subject):实现业务逻辑的核心对象。
- 代理(Proxy):持有真实对象引用,负责控制访问并扩展功能。
(3)典型场景
- 远程服务调用(如Web Service、gRPC代理)。
- 权限控制与安全校验(保护代理)。
- 延迟加载与资源优化(虚拟代理)。
- 日志记录与性能监控(动态代理)。
2、C#代码实现
(1)静态代理
- 实现要点
- 显式定义代理类,与真实对象共同实现同一接口。
- 手动添加额外逻辑(如权限检查、日志记录)。
- 应用场景:权限控制、日志增强等
- 代码示例(以媒婆代理为例)
public interface IGiveGift {void GiveFlowers();void GiveChocolate();
}
public class Pursuit : IGiveGift {private SchoolGirl girl;public Pursuit(SchoolGirl girl) => this.girl = girl;public void GiveFlowers() => Console.WriteLine($"送花给{girl.Name}");public void GiveChocolate() => Console.WriteLine($"送巧克力给{girl.Name}");
}
public class Proxy : IGiveGift {private Pursuit pursuit;public Proxy(SchoolGirl girl) => pursuit = new Pursuit(girl);public void GiveFlowers() {Console.WriteLine("[媒婆日志] 开始送花");pursuit.GiveFlowers();}public void GiveChocolate() {Console.WriteLine("[媒婆日志] 开始送巧克力");pursuit.GiveChocolate();}
}
SchoolGirl jiaojiao = new SchoolGirl { Name = "李娇娇" };
IGiveGift proxy = new Proxy(jiaojiao);
proxy.GiveFlowers();
(2)虚拟代理(延迟加载)
- 实现要点
- 延迟创建高开销对象,仅在首次访问时初始化真实对象。
- 适用于大文件加载、数据库连接等场景。
- 代码示例(图片加载代理)
public interface IImage {void Display();
}public class RealImage : IImage {private string _path;public RealImage(string path) {_path = path;LoadFromDisk(); }public void Display() => Console.WriteLine($"显示图片:{_path}");private void LoadFromDisk() => Console.WriteLine($"加载图片:{_path}");
}public class ProxyImage : IImage {private string _path;private RealImage _realImage;public ProxyImage(string path) => _path = path;public void Display() {if (_realImage == null) {_realImage = new RealImage(_path); }_realImage.Display();}
}
IImage image = new ProxyImage("large_image.jpg");
image.Display();
(3)保护代理(权限控制)
- 实现要点
- 验证访问权限,限制非法操作。
- 典型场景:敏感数据访问、API权限校验。
- 代码示例
public interface ISensitiveData {void AccessData();
}public class SensitiveData : ISensitiveData {public void AccessData() => Console.WriteLine("访问敏感数据");
}public class ProtectionProxy : ISensitiveData {private SensitiveData _data;private string _userRole;public ProtectionProxy(string role) => _userRole = role;public void AccessData() {if (_userRole == "Admin") {if (_data == null) _data = new SensitiveData();_data.AccessData();} else {Console.WriteLine("权限不足!");}}
}
ISensitiveData proxy = new ProtectionProxy("User");
proxy.AccessData();
(4)动态代理(运行时生成)
- 实现要点
- 通过反射或第三方库(如Castle DynamicProxy)自动生成代理类。
- 避免静态代理的代码冗余,适用于AOP编程。
- 示例(使用Castle.Core)
public class LogInterceptor : IInterceptor {public void Intercept(IInvocation invocation) {Console.WriteLine($"[日志] 调用方法:{invocation.Method.Name}");invocation.Proceed();}
}
var proxyGenerator = new ProxyGenerator();
var service = proxyGenerator.CreateInterfaceProxyWithTarget<IService>(new ServiceImpl(), new LogInterceptor()
);
service.Execute();
3、优缺点分析
(1)优点
- 解耦客户端与真实对象,扩展性强。
- 实现权限控制、延迟加载等非业务功能。
(2)缺点
(3)对比其他模式
| 模式 | 核心区别 |
|---|
| 装饰器模式 | 增强对象功能,关注添加职责。 |
| 适配器模式 | 转换接口,解决兼容性问题。 |
| 外观模式 | 封装子系统接口,简化复杂调用流程。 |
组合模式
1、定义与核心思想
(1)定义
- 组合模式是一种结构型设计模式,用于将对象组织成树形结构以表示“部分-整体”的层次关系。
- 其核心思想是通过统一接口处理单个对象(叶子)和组合对象(容器),使客户端无需区分操作目标的具体类型。
(2)核心角色
- Component(抽象组件):定义所有对象的公共接口,包含管理子组件的方法(如
Add、Remove)和操作(如 Display)。 - Leaf(叶子节点):示不可再分的对象,通常不包含子组件,仅实现操作逻辑。
- Composite(组合节点):包含子组件的容器对象,实现子组件管理和操作委托。
(3)应用场景
- 文件系统:文件和文件夹的树形结构是组合模式的典型应用,客户端可统一处理文件的读取、删除等操作。
- 组织架构:企业组织架构(如公司-部门-员工)可通过组合模式实现层级展示和管理。
- UI控件树:GUI框架中的容器(Panel)和控件(Button)可统一处理布局和渲染。
- 菜单系统:多级菜单(主菜单-子菜单-菜单项)的操作可递归执行。
2、C#代码实现
(1)透明模式实现
- 透明模式下,所有组件(包括叶子)统一实现管理子组件的方法,但叶子节点的相关方法可能为空或抛出异常。
- 这种设计简化了客户端调用,但可能违反接口隔离原则。
public abstract class Component
{public string Name { get; set; }public Component(string name){Name = name;}public abstract void Add(Component component);public abstract void Remove(Component component);public abstract void Display(int depth);
}
public class Employee : Component
{public Employee(string name) : base(name) { }public override void Add(Component component){throw new NotSupportedException("叶子节点无法添加子节点");}public override void Remove(Component component){throw new NotSupportedException("叶子节点无法移除子节点");}public override void Display(int depth){Console.WriteLine(new string('-', depth) + Name);}
}
public class Department : Component
{private List<Component> _children = new List<Component>();public Department(string name) : base(name) { }public override void Add(Component component){_children.Add(component);}public override void Remove(Component component){_children.Remove(component);}public override void Display(int depth){Console.WriteLine(new string('-', depth) + Name);foreach (var child in _children){child.Display(depth + 2);}}
}
(2)安全模式实现
- 安全模式下,管理子组件的方法仅在组合节点中实现,叶子节点无需支持这些操作。
- 客户端需明确区分叶子与容器,但更符合接口隔离原则。
public abstract class Component
{public string Name { get; set; }public Component(string name){Name = name;}public abstract void Display(int depth);
}
public interface IComposite
{void Add(Component component);void Remove(Component component);
}
public class Department : Component, IComposite
{private List<Component> _children = new List<Component>();public Department(string name) : base(name) { }public void Add(Component component){_children.Add(component);}public void Remove(Component component){_children.Remove(component);}public override void Display(int depth){Console.WriteLine(new string('-', depth) + Name);foreach (var child in _children){child.Display(depth + 2);}}
}
public class Employee : Component
{public Employee(string name) : base(name) { }public override void Display(int depth){Console.WriteLine(new string('-', depth) + Name);}
}
(3)扩展讨论
- 递归遍历优化:对于深层嵌套结构,可采用缓存、迭代器或备忘录模式优化递归性能。
- 组合模式与数据绑定:在MVVM框架中,组合模式可结合数据绑定实现动态UI更新。
(4)公司组织架构案例
public abstract class OrganizationComponent
{public string Name { get; set; }public OrganizationComponent(string name){Name = name;}public abstract void Add(OrganizationComponent component);public abstract void Remove(OrganizationComponent component);public abstract void Display(int depth);public abstract void SendNotice(string message);
}
public class Employee : OrganizationComponent
{public Employee(string name) : base(name) { }public override void Add(OrganizationComponent component){throw new NotSupportedException("员工无法添加下属");}public override void Remove(OrganizationComponent component){throw new NotSupportedException("员工无法移除下属");}public override void Display(int depth){Console.WriteLine($"{new string('-', depth)}员工:{Name}");}public override void SendNotice(string message){Console.WriteLine($"员工[{Name}]收到通知:{message}");}
}
public class Department : OrganizationComponent
{private List<OrganizationComponent> _children = new List<OrganizationComponent>();public Department(string name) : base(name) { }public override void Add(OrganizationComponent component){_children.Add(component);}public override void Remove(OrganizationComponent component){_children.Remove(component);}public override void Display(int depth){Console.WriteLine($"{new string('-', depth)}部门:{Name}");foreach (var child in _children){child.Display(depth + 2);}}public override void SendNotice(string message){Console.WriteLine($"部门[{Name}]发送通知:{message}");foreach (var child in _children){child.SendNotice(message);}}
}
class Program
{static void Main(){var root = new Department("总公司");root.Add(new Employee("CEO"));var hrDept = new Department("人力资源部");hrDept.Add(new Employee("HR经理"));hrDept.Add(new Employee("招聘专员"));root.Add(hrDept);var techDept = new Department("技术部");techDept.Add(new Employee("CTO"));var devTeam = new Department("开发组");devTeam.Add(new Employee("开发工程师A"));devTeam.Add(new Employee("开发工程师B"));techDept.Add(devTeam);root.Add(techDept);root.Display(1);root.SendNotice("本周五召开全员会议");}
}
3、优缺点分析
(1)优点
- 统一接口:客户端无需区分叶子与容器,简化调用逻辑。
- 灵活扩展:新增组件类型无需修改现有代码,符合开闭原则。
- 树形结构支持:天然支持递归遍历和复杂层级操作。
(2)缺点
- 接口冗余:透明模式可能导致叶子节点实现无意义的方法。
- 性能开销:递归遍历可能影响性能,需谨慎设计层级深度。
- 复杂性增加:设计初期需明确对象层次,可能增加系统复杂度。
(3)组合模式 vs 装饰器模式
- 组合模式:处理对象间的层次结构(部分-整体)。
- 装饰器模式:动态扩展对象功能(附加职责)。