用c#库类组成一个通用接口

时间:2018-11-29 01:29:38

标签: c# oop design-patterns dependency-injection interface

假设我有一堂课:

public class Foo<TKey, TValue> {
    private readonly ConcurrentDictionary<TKey, TValue> m_dict;
    public Foo() {
        m_dict = new ConcurrentDictionary<TKey, TValue>();
    }
    public void AddThings(TKey key, TValue val) {
        m_dict.Add(key, val);
    }
    public void RemoveThings(TKey key) {
        m_dict.Remove(key);
    }
    //}

这是ConcurrentDictionary的API:https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=netframework-4.7.2 它实现了以下接口:

public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IEnumerable, IDictionary, ICollection, IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<KeyValuePair<TKey, TValue>>

基本上,我的Foo类使用ConcurrentDictionary的API方法的子集。

现在,在某些使用情况下,需要在客户端类中使用ConcurrentTreeMap<TKey, TValue>。我已经使用客户端类所需的所有API方法自己实现了ConcurrentTreeMap<TKey, TValue>类。此类实现IDictionary<TKey, TValue>ICollection<KeyValuePair<TKey, TValue>>

我希望我的Foo客户端类能够同时使用ConcurrentDictionary和ConcurrentTreeMap。像这样的东西,简化了:

public class Foo<TKey, TValue> {
        private readonly IConcurrentDictionary<TKey, TValue> m_dict;
        public Foo(IConcurrentDictionary dict) {
            m_dict = dict;
        }
        public void AddThings(TKey key, TValue val) {
            m_dict.Add(key, val);
        }
        public void RemoveThings(TKey key) {
            m_dict.Remove(key);
        }
        //}

对于Python和Go这样的语言,这将很容易。如果我可以访问ConcurrentDictionary的实现,则显然可以提取两者之间的通用接口类型。或者,我可以定义ConcurrentDictionary实现的所有接口的复合接口。但是由于ConcurrentDictionary是某些基本c#库的一部分,所以我不能这样做。我应该在ConcurrentDictionary和自定义接口之间使用某种类型的代理类吗?看来我必须编写很多样板代码。

树图和哈希图(字典)之间的支持数据完全不同,因此我也无法从ConcurrentDictionary继承ConcurrentTreeMap(无论如何,大多数方法都不是虚拟的)。

1 个答案:

答案 0 :(得分:2)

Foo<TKey, TValue>本质上是一个集合的包装器。如果要在其他集合周围使用包装器,那么最好的方法可能是创建一个接口:

public interface IFoo<TKey, TValue>
{
    void AddThings(TKey key, TValue val);
    void RemoveThings(TKey key);
}

...并且有两个单独的实现-一个使用内部ConcurrentDictionary,另一个使用您的ConcurrentTreeMap<TKey, TValue>

这是一个实现-我不知道另一个实现,因为我没有您的ConcurrentTree类:

public class ConcurrentDictionaryFoo : IFoo<TKey, TValue>
{
    private readonly ConcurrentDictionary<TKey, TValue> _dictionary 
        = new ConcurrentDictionary<TKey, TValue>();

    void AddThings(TKey key, TValue val)
    {
        _dictionary.TryAdd(key, val);
    }

    void RemoveThings(TKey key)
    {
        _dictionary.TryRemove(key);
    }
}

当我们有一个接口和多个实现时,这是最常见的做法。通常,我们通常不会尝试将两个实现都放在一个类中。

您可以将它们都放在一个类中。您可以在一个类中同时包含一个ConcurrentDictionary和一个ConcurrentTreeMap,然后构造函数中的某个标志会告诉该类应使用哪个内部集合。

但是,如果我们遵循那一条路,那么如果您需要另一个使用不同集合的实现,又一个又一个,那会发生什么呢?如果要创建IFoo的不同实现,这是一个很容易解决的问题,但是如果您尝试将所有可能的实现都放在一个类中,则会很麻烦。

如果此类很复杂并且集合只是其中的一小部分,而又不想仅因为希望它可以与任何一种集合类型一起工作,就不想重复其余代码,该怎么办?

在那种情况下,您仍将创建一个代表集合的接口,并为ConcurrentDictionaryConcurrentTreeMap分别实现。然后Foo将取决于该抽象,而不取决于任何一个具体的集合。像这样:

public class Foo<TKey, TValue>
{
    private readonly IFooDictionary<TKey, TValue> _dictionary;

    public Foo(IFooDictionary<TKey, TValue> dictionary)
    {
        _dictionary = dictionary;
    }
}

现在Foo仅依赖于IFooDictionary,并且不知道基础实现是ConcurrentDictionary还是ConcurrentTreeMap

这与您所说的相符:

  

如果我可以访问ConcurrentDictionary的实现,则显然可以提取两者之间的通用接口类型。

除了公用接口不是您要提取的东西以外,它是您创建的东西。您定义一个接口,该接口描述您的其他类需要使用此接口的对象。然后,您只需在ConcurrentDictionaryConcurrentTreeMap周围创建一个适配器或包装器即可将接口的方法定向到内部集合的正确方法,就像上面的示例中的AddThings方法调用内部字典的TryAdd方法。