C#设计模式--装饰器模式(Decorator Pattern)

装饰器模式是一种结构型设计模式,它允许在运行时动态地给一个对象添加职责,而不需要修改对象的结构。
装饰器模式通过创建一个包装对象来包裹真实的对象,从而在不改变对象接口的情况下增加新的行为或责任。

案例 1:日志记录器

场景描述

在日志记录系统中,我们可能需要在不同的地方记录日志,例如控制台、文件、数据库等。使用装饰器模式可以动态地添加不同的记录方式,而不需要修改原有的日志记录逻辑。

代码实现

csharp 复制代码
using System;
using System.IO;

// 抽象的日志记录器接口
public interface ILogger
{
    void Log(string message);
}

// 具体的日志记录器实现
public class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"Console: {message}");
    }
}

// 装饰器基类
public abstract class LoggerDecorator : ILogger
{
    protected readonly ILogger _logger;

    public LoggerDecorator(ILogger logger)
    {
        _logger = logger;
    }

    public virtual void Log(string message)
    {
        _logger.Log(message);
    }
}

// 文件日志记录器装饰器
public class FileLoggerDecorator : LoggerDecorator
{
    public FileLoggerDecorator(ILogger logger) : base(logger) { }

    public override void Log(string message)
    {
        base.Log(message);
        using (StreamWriter writer = new StreamWriter("log.txt", true))
        {
            writer.WriteLine($"File: {message}");
        }
    }
}

// 数据库日志记录器装饰器
public class DatabaseLoggerDecorator : LoggerDecorator
{
    public DatabaseLoggerDecorator(ILogger logger) : base(logger) { }

    public override void Log(string message)
    {
        base.Log(message);
        // 假设这里有一个数据库连接
        Console.WriteLine($"Database: {message}");
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        ILogger logger = new ConsoleLogger();
        logger = new FileLoggerDecorator(logger);
        logger = new DatabaseLoggerDecorator(logger);

        logger.Log("This is a log message.");
        // 输出:
        // Console: This is a log message.
        // File: This is a log message.
        // Database: This is a log message.
    }
}

类型图:

ILogger +void Log(string message) ConsoleLogger +void Log(string message) LoggerDecorator -ILogger _logger +void Log(string message) FileLoggerDecorator +void Log(string message) DatabaseLoggerDecorator +void Log(string message)

代码解析

  1. ILogger 接口:定义了日志记录的基本方法。
  2. ConsoleLogger 类:实现了 ILogger 接口,将日志记录到控制台。
  3. LoggerDecorator 类:装饰器基类,包含一个 ILogger 对象,并提供了默认的 Log 方法。
  4. FileLoggerDecorator 类:具体的装饰器,将日志记录到文件。
  5. DatabaseLoggerDecorator 类:具体的装饰器,将日志记录到数据库。
  6. 客户端代码:创建日志记录器,并通过装饰器动态地添加不同的记录方式。

案例 2:咖啡店的饮料装饰

场景描述

在一家咖啡店中,顾客可以选择不同的基础饮料(如咖啡、茶),并可以添加不同的调料(如糖、奶)。使用装饰器模式可以动态地添加调料,而不需要为每一种组合创建一个新的类。

代码实现

csharp 复制代码
using System;

// 抽象的饮料接口
public interface IBeverage
{
    string Description { get; }
    double Cost { get; }
}

// 具体的饮料实现
public class Coffee : IBeverage
{
    public string Description => "Coffee";
    public double Cost => 2.00;
}

public class Tea : IBeverage
{
    public string Description => "Tea";
    public double Cost => 1.50;
}

// 装饰器基类
public abstract class BeverageDecorator : IBeverage
{
    protected readonly IBeverage _beverage;

    public BeverageDecorator(IBeverage beverage)
    {
        _beverage = beverage;
    }

    public virtual string Description => _beverage.Description;
    public virtual double Cost => _beverage.Cost;
}

// 添加糖的装饰器
public class SugarDecorator : BeverageDecorator
{
    public SugarDecorator(IBeverage beverage) : base(beverage) { }

    public override string Description => $"{_beverage.Description}, Sugar";
    public override double Cost => _beverage.Cost + 0.50;
}

// 添加奶的装饰器
public class MilkDecorator : BeverageDecorator
{
    public MilkDecorator(IBeverage beverage) : base(beverage) { }

    public override string Description => $"{_beverage.Description}, Milk";
    public override double Cost => _beverage.Cost + 0.75;
}

// 使用示例
public class Program
{
    public static void Main()
    {
        IBeverage coffee = new Coffee();
        coffee = new SugarDecorator(coffee);
        coffee = new MilkDecorator(coffee);

        Console.WriteLine($"{coffee.Description}: ${coffee.Cost}");
        // 输出: Coffee, Sugar, Milk: $3.25
    }
}

类型图:

IBeverage +string Description +double Cost Coffee +string Description +double Cost Tea +string Description +double Cost BeverageDecorator -IBeverage _beverage +string Description +double Cost SugarDecorator +string Description +double Cost MilkDecorator +string Description +double Cost

代码解析

  1. IBeverage 接口:定义了饮料的基本属性和方法。
  2. Coffee 类 和 Tea 类:具体的饮料实现。
  3. BeverageDecorator 类:装饰器基类,包含一个 IBeverage 对象,并提供了默认的 Description 和 Cost 方法。
  4. SugarDecorator 类 和 MilkDecorator 类:具体的装饰器,分别添加糖和奶。
  5. 客户端代码:创建饮料,并通过装饰器动态地添加调料。

案例 3:用户权限管理

场景描述

在用户权限管理系统中,用户可能有不同的角色(如普通用户、管理员),并且每个角色可能有额外的权限(如审核、删除)。使用装饰器模式可以动态地为用户添加不同的权限,而不需要为每一种角色创建一个新的类。

代码实现

csharp 复制代码
using System;
using System.Collections.Generic;

// 抽象的用户接口
public interface IUser
{
    string Name { get; }
    List<string> Permissions { get; }
    void GrantPermission(string permission);
}

// 具体的用户实现
public class BasicUser : IUser
{
    public string Name { get; }
    public List<string> Permissions { get; } = new List<string>();

    public BasicUser(string name)
    {
        Name = name;
    }

    public void GrantPermission(string permission)
    {
        Permissions.Add(permission);
    }
}

// 装饰器基类
public abstract class UserDecorator : IUser
{
    protected readonly IUser _user;

    public UserDecorator(IUser user)
    {
        _user = user;
    }

    public virtual string Name => _user.Name;
    public virtual List<string> Permissions => _user.Permissions;
    public virtual void GrantPermission(string permission) => _user.GrantPermission(permission);
}

// 管理员装饰器
public class AdminDecorator : UserDecorator
{
    public AdminDecorator(IUser user) : base(user) { }

    public override void GrantPermission(string permission)
    {
        base.GrantPermission(permission);
        if (!Permissions.Contains("Admin"))
        {
            Permissions.Add("Admin");
        }
    }
}

// 审核员装饰器
public class AuditorDecorator : UserDecorator
{
    public AuditorDecorator(IUser user) : base(user) { }

    public override void GrantPermission(string permission)
    {
        base.GrantPermission(permission);
        if (!Permissions.Contains("Audit"))
        {
            Permissions.Add("Audit");
        }
    }
}

// 使用示例
public class Program
{
    public static void Main()
    {
        IUser user = new BasicUser("John Doe");
        user = new AdminDecorator(user);
        user = new AuditorDecorator(user);

        user.GrantPermission("Read");
        user.GrantPermission("Write");

        Console.WriteLine($"{user.Name} has permissions: {string.Join(", ", user.Permissions)}");
        // 输出: John Doe has permissions: Read, Write, Admin, Audit
    }
}

类型图:

IUser +string Name +List Permissions +void GrantPermission(string permission) BasicUser +string Name +List Permissions +void GrantPermission(string permission) UserDecorator -IUser _user +string Name +List Permissions +void GrantPermission(string permission) AdminDecorator +void GrantPermission(string permission) AuditorDecorator +void GrantPermission(string permission)

代码解析

  1. IUser 接口:定义了用户的属性和方法。
  2. BasicUser 类:具体的用户实现,包含基本的权限管理。
  3. UserDecorator 类:装饰器基类,包含一个 IUser 对象,并提供了默认的属性和方法。
  4. AdminDecorator 类 和 AuditorDecorator 类:具体的装饰器,分别为用户添加管理员和审核员权限。
  5. 客户端代码:创建用户,并通过装饰器动态地添加不同的权限。

优点

  1. 灵活性:可以在运行时动态地添加或移除职责,增加了系统的灵活性。
  2. 扩展性:通过装饰器类可以轻松地扩展对象的功能,而不需要修改原有的类。
  3. 符合开闭原则:装饰器模式遵循开闭原则,即对扩展开放,对修改关闭。

缺点

  1. 类的数量增加:每个装饰器都是一个类,随着装饰器数量的增加,类的数量也会增加,可能导致系统复杂度上升。
  2. 调试困难:由于装饰器的嵌套层次可能较深,调试时可能会比较困难。
  3. 性能开销:每次调用装饰器的方法时都会有一定的性能开销,尤其是在多层装饰器的情况下。
    通过以上三个案例,我们可以看到装饰器模式在实际开发中的应用及其优缺点。希望这些示例能帮助你更好地理解和应用装饰器模式。
相关推荐
凌盛羽1 小时前
C#对Excel表csv文件的读写操作
开发语言·windows·物联网·microsoft·c#·excel
CV大法好3 小时前
刘铁猛p3 C# 控制台程序引用System.Windows.Forms报错,无法引用程序集 解决方法
开发语言·c#
VinciYan4 小时前
C#中通过ObjectPool重用对象提高程序性能
c#·asp.net·.net·.netcore
西岭千秋雪_4 小时前
设计模式の单例&工厂&原型模式
java·单例模式·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式·原型模式
m0_748251726 小时前
Spring Boot 经典九设计模式全览
java·spring boot·设计模式
小码编匠7 小时前
C#上位机实现高效示波器功能
后端·c#·.net
小码编匠7 小时前
WPF 制作雷达扫描图
后端·c#·.net
qq_10799104057 小时前
E172 ASP.NET+SQL+C#+LW+图书管理系统的设计与实现 配置 源码 文档 全套资料
sql·c#·asp.net
fuvuof8 小时前
设计模式——单例模式和工厂模式
python·单例模式·设计模式