实现带有缓存的接口

问题描述 投票:0回答:1

在下面的示例代码中,我有一个接口

IService
,它只有一个用户需要实现的方法
GetDescriptors

我还添加了常见查询的辅助方法,以便更轻松地使用

IService
。我用两种不同的方式展示了这一点,界面本身的方法,然后也通过扩展方法。

虽然这有效,但想象一下,无论出于何种原因,

GetDescriptorsByGroup
实际上是一个昂贵的调用,而不是每次调用时计算它的值,我们希望惰性地评估它并缓存结果以在将来的调用中返回(结果永远不会改变)。

有什么方法可以实现这一点,而不必在每个实现

GetDescriptorsByGroup
的类上实现
IService

我有很多类实现

IService
,包括 Mocks,因此必须实现
GetDescriptorsByGroup
的重复代码将不太理想。我曾考虑过一个基类,..但是这对于模拟不起作用。

public class Descriptor
{
    public string Name { get; }
    public string Group { get; }
    public int Count { get; }
    public string[] Dependencies { get; }

    public Descriptor(string name, string group, int count, string[] dependencies)
    {
        Name = name;
        Group = group;
        Count = count;
        Dependencies = dependencies;
    }
}

public interface IService
{
    IReadOnlyList<Descriptor> GetDescriptors();

    // helpers
    IEnumerable<Descriptor> GetDescriptorsByName(string name) => GetDescriptors().Where(e => e.Name == name);
    IEnumerable<Descriptor> GetDescriptorsByGroup(string group) => GetDescriptors().Where(e => e.Group == group);
}

// more helpers
public static class IServiceExtensions
{
    public static IEnumerable<Descriptor> GetDescriptorsCount(this IService service, int count) => service.GetDescriptors().Where(e => e.Count == count);
    public static IEnumerable<string> GetAllDependencies(this IService service) => service.GetDescriptors().SelectMany(e => e.Dependencies);
}

public class ServiceA : IService
{
    private readonly List<Descriptor> _descriptors;

    public ServiceA()
    {
        // compute descriptors here ...
        _descriptors = new List<Descriptor>();
    }

    public IReadOnlyList<Descriptor> GetDescriptors() => _descriptors;
}

public class ServiceB : IService
{
    private readonly List<Descriptor> _descriptors;

    public ServiceB()
    {
        // compute descriptors here ...
        _descriptors = new List<Descriptor>();
    }

    public IReadOnlyList<Descriptor> GetDescriptors() => _descriptors;
}


internal class Program
{
    static void Main(string[] args)
    {
        IService service = new ServiceA();

        var x = service.GetDescriptorsByName("A");
        var y = service.GetAllDependencies();
    }
}
c# interface
1个回答
1
投票

你能做的是装饰者:

public sealed class CachingService : IService
{
    private readonly IService _valueSource;
    private readonly SomeCacheImpl<object key, Lazy<IReadOnlyList<Descriptor>>> _cache;
    private readonly object _key = new(); // In this case, there is only one key...

    public CachingService(IService valueSource)
    {
        _valueSource = valueSource;
        _cache = new(); // Or inject ...
    }

    public IReadOnlyList<Descriptor> GetDescriptors()
      => _cache.GetOrAdd(_key, _key => new Lazy<IReadOnlyList<Descriptor>>(() => _valueSource.GetDescriptors()).Value;
}

然后您可以注入而不是具体的 IService 实现:

static void Main(string[] args)
{
    IService service = new CachingService(new ServiceA());

    var x = service.GetDescriptorsByName("A");
    var y = service.GetAllDependencies();
}
© www.soinside.com 2019 - 2024. All rights reserved.