装饰器模式
是一种结构型设计模式
,它允许在运行时动态地给一个对象添加职责,而不需要修改对象的结构。
装饰器模式通过创建一个包装对象来包裹真实的对象,从而在不改变对象接口的情况下增加新的行为或责任。
案例 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)
代码解析
- ILogger 接口:定义了日志记录的基本方法。
- ConsoleLogger 类:实现了 ILogger 接口,将日志记录到控制台。
- LoggerDecorator 类:装饰器基类,包含一个 ILogger 对象,并提供了默认的 Log 方法。
- FileLoggerDecorator 类:具体的装饰器,将日志记录到文件。
- DatabaseLoggerDecorator 类:具体的装饰器,将日志记录到数据库。
- 客户端代码:创建日志记录器,并通过装饰器动态地添加不同的记录方式。
案例 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
代码解析
- IBeverage 接口:定义了饮料的基本属性和方法。
- Coffee 类 和 Tea 类:具体的饮料实现。
- BeverageDecorator 类:装饰器基类,包含一个 IBeverage 对象,并提供了默认的 Description 和 Cost 方法。
- SugarDecorator 类 和 MilkDecorator 类:具体的装饰器,分别添加糖和奶。
- 客户端代码:创建饮料,并通过装饰器动态地添加调料。
案例 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)
代码解析
- IUser 接口:定义了用户的属性和方法。
- BasicUser 类:具体的用户实现,包含基本的权限管理。
- UserDecorator 类:装饰器基类,包含一个 IUser 对象,并提供了默认的属性和方法。
- AdminDecorator 类 和 AuditorDecorator 类:具体的装饰器,分别为用户添加管理员和审核员权限。
- 客户端代码:创建用户,并通过装饰器动态地添加不同的权限。
优点
- 灵活性:可以在运行时动态地添加或移除职责,增加了系统的灵活性。
- 扩展性:通过装饰器类可以轻松地扩展对象的功能,而不需要修改原有的类。
- 符合开闭原则:装饰器模式遵循开闭原则,即对扩展开放,对修改关闭。
缺点
- 类的数量增加:每个装饰器都是一个类,随着装饰器数量的增加,类的数量也会增加,可能导致系统复杂度上升。
- 调试困难:由于装饰器的嵌套层次可能较深,调试时可能会比较困难。
- 性能开销:每次调用装饰器的方法时都会有一定的性能开销,尤其是在多层装饰器的情况下。
通过以上三个案例,我们可以看到装饰器模式在实际开发中的应用及其优缺点。希望这些示例能帮助你更好地理解和应用装饰器模式。