C # Generics и абстрактный шаблон фабрики - или какой-то способ сделать что-то подобное - PullRequest
2 голосов
/ 16 июля 2010

Я пытаюсь написать своего рода расширяемый слой данных для моего приложения. Один из «репозиториев» - это реализация в моем классе абстрактных хранилищ

public abstract class Store<TEntity, TIdentifier> : IStore<TEntity, TIdentifier>
        where TEntity : StorableEntity<TIdentifier>        
{
  //abstract methods here

  public abstract TIdentifier GetUniqueIdentifier();
}

Абстрактный класс «StorableEntity»:

public abstract class StorableEntity<TIdentifier>
{
    public TIdentifier ID { get; set; }
}

У меня есть конкретный класс Store с именем "InMemoryStore", который выглядит следующим образом:

public class InMemoryStore<T, U> : Store<T, U>
    where T : StorableEntity<U>
{
    protected static Dictionary<U, T> store = new Dictionary<U, T>();

    public override U GetUniqueIdentifier()
    {
    // call relevant "generator" here - SOMETHING LIKE THIS??
    // var generator = GetGeneratorSomehow(U);
    // return generator.Create();
    }
}

Теперь тип "U" здесь может быть string, int,Guid и т. Д. (В большинстве случаев это может быть int)

Моя идея заключается в том, чтобы создать что-то вроде IUIDGenerator, например:

public interface IUIDGenerator<T>
{
    T Create(ICollection<T> collection);
}

В приведенном выше InMemoryStore язатем создаст экземпляр IUIDGenerator, передаст коллекцию ключей store dictionarys и вызовет метод «Create» для возврата уникального идентификатора требуемого типа.

Например, у меня может быть класс IntUIDGenerator, напримерэто (это будет действовать как своего рода генератор добавочных чисел, основанный на числах, уже имеющихся в словарных ключах)

public class IntUIDGenerator : IUIDGenerator<int>
{
    public int Create(ICollection<int> collection)
    {
        var result = collection.Max() + 1;
        if (collection.Contains(result))
            return result;

        throw new NotUniqueException();
    }
}

Актуальный вопрос: Что мне нужно сделать, это св InMemoryStore определите тип U (тип идентификатора) и сможете динамически выбирать требуемую конкретную реализацию IUIDGenerator - как я могу это сделать?

Я думал о том, чтобы иметь тип шаблона фабрики классов- загрузка всех доступных UIDGenerators в словарь ... но все они могут иметь разные типы?

Есть ли лучший способ обойти это?

Кроме того, я знаю название моего вопросаможет быть немного неуместным - если у кого-то есть лучшее предложение, пожалуйста, не стесняйтесь комментировать, и я изменю его.

Ответы [ 4 ]

2 голосов
/ 16 июля 2010

Вы можете использовать инфраструктуру IoC, такую ​​как Unity , Castle, Ninject и т. Д. Затем вы настроите свой контейнер следующим образом:

_container = new UnityContainer();
_container.RegisterType<IUIDGenerator<int>, IntUIDGenerator);
_container.RegisterType<IUIDGenerator<Guid>, GuidUIDGenerator);

Тогда в вашем классе у вас будет что-то вроде следующего:

public override U GetUniqueIdentifier()
{
    var generator = _container.Resolve<IUIDGenerator<U>>();
    return generator.Create();
}
0 голосов
/ 16 июля 2010

Вы можете добавить дополнительный аргумент универсального типа для генератора, например

public class InMemoryStore<T, U, V> : Store<T, U>
    where T : StorableEntity<U>
    where V : IUIDGenerator<U>, new() {

    public override U GetUniqueIdentifier()
    {
        return (V)(Activator.CreateInstance<V>()).Create();
    }
}
0 голосов
/ 16 июля 2010

Краткий ответ: используйте внедрение зависимостей , и пусть ваш DI-контейнер обработает его для вас.

0 голосов
/ 16 июля 2010

Я думаю, вам придется использовать хотя бы один каст.Если вы храните все свои генераторы на карте, например

Map<Type, object> generators;

, вы можете использовать

class InMemoryStore<T,U> {
  public override U GetUniqueIdentifier() {
    var generator = generators[typeof(U)] as IUIDGenerator<U>;
    return generator.Create(collection);
  }
}

Конечно, я пропустил весь вид проверочного кода :) (например, проверка, если генератордля типа U есть на карте и т.д ...)

...