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
, но она будет сложной и запутанной, если вы попытаетесь поместить все возможные реализации в один класс.
Что если этот класс сложныйи коллекция - это лишь небольшая его часть, и вы не хотите дублировать остальную часть кода только потому, что хотите, чтобы он работал с любым типом коллекции?
В этом случае вы все равно создадите интерфейс для представления коллекции и будете иметь отдельные реализации для ConcurrentDictionary
и ConcurrentTreeMap
.Тогда 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, я бы, очевидно, просто извлек бы общий тип интерфейса между ними.
За исключением этого общего интерфейсаэто не то, что вы извлекаете - это то, что вы создаете.Вы определяете интерфейс, который описывает, для чего другие ваши классы должны использовать эту вещь.Затем вы просто создаете адаптер или оболочку для ConcurrentDictionary
или ConcurrentTreeMap
, которая направляет методы интерфейса на правильные методы внутренней коллекции - как в примере выше, где метод AddThings
вызывает TryAdd
метод внутреннего словаря.