C#使用 Scrutor 快速实现“装饰者模式”

装饰者模式介绍

装饰器模式(Decorator Pattern)是在不改变原类和使用继承的情况下,动态地给一个对象添加一些额外的职责。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。.

可以在如下使用场景中使用装饰器模式:

  • 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。

  • 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。

  • 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

Demo

假设,我们使用IRepository接口的实现类Repository进行数据库访问:

public interface IUserRepository
{
    User Get(int Id);
}

public class UserRepository : IUserRepository
{
    public User Get(int Id)
    {
        Console.WriteLine("访问数据库");

        //模拟数据库访问
        return new User(Id, "My IO");
    }
}

现在有一个新的需求:要求增加缓存功能,即先从缓存获取数据,缓存不存在再访问数据库。

有几种方法可以做到这一点:

  1. 修改业务逻辑,在访问Get前读取缓存;
  2. 使用面向切面(AOP)模式,在调用Get方法时注入读取缓存逻辑;
  3. 修改UserRepository实现缓存功能;
  4. 继承UserRepository类,在派生类中实现缓存功能;
  5. 使用装饰器模式封装UserRepository类,在新的UserRepositoryDecorator类中嵌入实现缓存功能;

在这里,我们使用装饰器模式:

internal class UserRepositoryCacheDecorator : IUserRepository
{
    private readonly IUserRepository _userRepository;

    public UserRepositoryCacheDecorator(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public User Get(int Id)
    {
        Console.WriteLine("访问缓存");
        return _userRepository.Get(Id);
    }
}
  • 首先,我们还是实现与UserRepository类相同的IUserRepository接口;
  • 其次,我们将IUserRepository接口注入到构造函数中;
  • 最后,在Get方法中,我们再次调用注入的接口的Get方法,它应该调用UserRepository类的实现。

显然,如果还是按普通方式进行依赖注入,将不可能成功:

builder.Services.AddTransient<IUserRepository, UserRepository>();

builder.Services.AddTransient<IUserRepository, UserRepositoryCacheDecorator>();

C#使用 Scrutor 快速实现“装饰者模式”

这时,我们可以引用 Nuget 包Scrutor,然后使用它提供的Decorate方法:

builder.Services.AddTransient<IUserRepository, UserRepository>();

builder.Services.Decorate<IUserRepository, UserRepositoryCacheDecorator>();

现在再运行,你将看到装饰器模式已经实现了:

C#使用 Scrutor 快速实现“装饰者模式”

结论

可以添加多个装饰,为原来的功能增加不同的职责,而仅需的操作,就是调用Decorate方法进行注册:

builder.Services.Decorate<IUserRepository, UserRepositoryCacheDecorator>();

builder.Services.Decorate<IUserRepository, UserRepositoryLogDecorator>();