结构型设计模式2

文章目录

享元模式

1、定义与核心思想

(1)定义
  • 享元模式(Flyweight Pattern)是一种结构型设计模式,其核心在于通过共享技术减少对象数量,降低内存消耗,适用于存在大量相似对象的场景。
  • 该模式将对象状态分为内部状态(Intrinsic)和外部状态(Extrinsic),其中内部状态可共享,而外部状态由客户端动态传递。
(2)核心目标
  • 内存优化:减少重复对象的创建。
  • 性能提升:通过共享对象避免重复计算。
(3)适用场景
  • 系统需要创建大量相似对象(如文本编辑器中的字符、游戏中的粒子特效)。
  • 对象的大部分状态可外部化,且外部状态与内部状态分离。
(4)应用场景
  • 图形渲染系统:共享相同的材质和纹理数据,位置和旋转角度作为外部状态。
  • 数据库连接池:复用数据库连接对象,避免频繁创建销毁。
  • 游戏开发:子弹、敌人等大量重复对象的实例共享。
  • 字符串驻留池:.NET 中的字符串驻留机制是享元模式的典型实现。

2、结构与角色

(1)抽象享元(Flyweight Interface)
  • 定义对象的操作接口,通常包含处理外部状态的方法。
csharp 复制代码
public interface IFlyweight
{
    void Operation(string extrinsicState);
}
(2)具体享元(Concrete Flyweight)
  • 实现抽象接口,存储内部状态(不可变)。
csharp 复制代码
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)
  • 管理共享对象池,确保相同内部状态的对象只创建一次。
csharp 复制代码
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)基础案例
  • 场景:模拟文本编辑器中的字符渲染,相同字体的字符共享内部状态(字体、颜色),位置由外部状态决定。
csharp 复制代码
// 抽象享元
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);
    }
}
  • 输出:
plain 复制代码
在位置(10,20)渲染字体: Arial, 颜色: Red  
在位置(30,40)渲染字体: Arial, 颜色: Red  
(2)关键优化
  • 线程安全:使用 ConcurrentDictionary 管理共享池。
csharp 复制代码
private readonly ConcurrentDictionary<string, Lazy<IFlyweight>> _flyweights = new();
  • 外部状态管理:将可变状态(如位置、大小)通过方法参数传递,而非存储于对象内部。
  • 组合模式扩展:对于复杂对象,可将享元与非享元对象组合使用。
csharp 复制代码
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 或自定义池实现高效对象复用:
csharp 复制代码
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)静态代理
  • 实现要点
    • 显式定义代理类,与真实对象共同实现同一接口。
    • 手动添加额外逻辑(如权限检查、日志记录)。
  • 应用场景:权限控制、日志增强等
  • 代码示例(以媒婆代理为例)
csharp 复制代码
// 抽象接口:送礼行为
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)虚拟代理(延迟加载)
  • 实现要点
    • 延迟创建高开销对象,仅在首次访问时初始化真实对象。
    • 适用于大文件加载、数据库连接等场景。
  • 代码示例(图片加载代理)
csharp 复制代码
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权限校验。
  • 代码示例
csharp 复制代码
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)
csharp 复制代码
// 定义拦截器
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(抽象组件):定义所有对象的公共接口,包含管理子组件的方法(如 AddRemove)和操作(如 Display)。
  • Leaf(叶子节点):示不可再分的对象,通常不包含子组件,仅实现操作逻辑。
  • Composite(组合节点):包含子组件的容器对象,实现子组件管理和操作委托。
(3)应用场景
  • 文件系统:文件和文件夹的树形结构是组合模式的典型应用,客户端可统一处理文件的读取、删除等操作。
  • 组织架构:企业组织架构(如公司-部门-员工)可通过组合模式实现层级展示和管理。
  • UI控件树:GUI框架中的容器(Panel)和控件(Button)可统一处理布局和渲染。
  • 菜单系统:多级菜单(主菜单-子菜单-菜单项)的操作可递归执行。

2、C#代码实现

(1)透明模式实现
  • 透明模式下,所有组件(包括叶子)统一实现管理子组件的方法,但叶子节点的相关方法可能为空或抛出异常。
  • 这种设计简化了客户端调用,但可能违反接口隔离原则。
csharp 复制代码
// 抽象组件
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)安全模式实现
  • 安全模式下,管理子组件的方法仅在组合节点中实现,叶子节点无需支持这些操作。
  • 客户端需明确区分叶子与容器,但更符合接口隔离原则。
csharp 复制代码
// 抽象组件(仅定义操作)
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)公司组织架构案例
  • 以下是一个完整的企业组织架构实现示例:
csharp 复制代码
// 抽象组件
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 装饰器模式
  • 组合模式:处理对象间的层次结构(部分-整体)。
  • 装饰器模式:动态扩展对象功能(附加职责)。
相关推荐
xian_wwq2 小时前
【学习笔记】《孙子兵法》与网络安全
网络·笔记·学习
猫头虎2 小时前
永久免费白嫖多个域名,一键托管Cloudflare,免费申请SSL加密证书,轻松建站、搭建线路伪装
服务器·开发语言·网络·数据库·python·网络协议·ssl
德迅云安全杨德俊2 小时前
服务器为何成为网络攻击的“重灾区“?
网络·安全·web安全·ddos
真正的醒悟2 小时前
什么是安全设备组网
服务器·数据库·php
wyzqhhhh3 小时前
WebSocket
网络·websocket·网络协议
小哈里3 小时前
【软考架构】2025H2系统架构设计师考试复习.jpg(软件架构、软件工程、数据库、Web开发、高项)
数据库·架构·系统架构·软件工程·后端开发
B站_计算机毕业设计之家3 小时前
深度学习:Yolo水果检测识别系统 深度学习算法 pyqt界面 训练集测试集 深度学习 数据库 大数据 (建议收藏)✅
数据库·人工智能·python·深度学习·算法·yolo·pyqt
爱莉希雅&&&3 小时前
DNS分离解析案例
运维·网络·dns
铭哥的编程日记3 小时前
【Linux网络】五种IO模型与非阻塞IO
linux·服务器·网络·tcp/ip·udp