Как вы делаете Generic Generic Factory? - PullRequest
3 голосов
/ 02 августа 2010

Я работаю над интерфейсом клиента (Silverlight) для коллекции webmethod.И я пытаюсь избежать написания какого-либо специального кода для каждого веб-метода.Поэтому я создал ServiceCall<TResult> для обработки каждого вызова, TResult указывает тип возврата службы (я использую XmlSerializer для создания возвращаемого экземпляра).Клиентский класс предоставляет функцию, соответствующую каждому веб-методу, и все, что эта функция должна сделать, - это создать новый экземпляр ServiceCall<TResult>, с TResult, указанным в качестве ожидаемого возвращаемого типа метода.И это прекрасно работает.

Я также использую Ninject (инжектор зависимостей), чтобы попытаться сохранить все независимым.Ninject поддерживает открытые дженерики, которые прекрасно работают с моим ServiceCall<TResult>.

Но это также означает, что я внедряю ссылку на контейнер Ninject.Что скрывает зависимость от привязки ServiceCall<TResult> к контейнеру.Поэтому я хотел бы вместо этого ввести фабрику для создания моих ServiceCall<TResult> экземпляров.Что не сложно, но я хотел бы сделать это универсальной универсальной фабрикой.Это означает, что я хотел бы иметь что-то вроде Factory<T<>>, в котором был бы метод public T<TU> Createinstance<TU>().

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

Это событие возможно в .Net?- Или мне нужно создать конкретный ServiceCallFactory?

Редактировать: Я использую интерфейсы для зависимостей, но нет необходимости смешивать их в вопросе здесь, поэтому я отредактировал этовопроса.

В ответ на Timwi: Распространенный способ использования внедрения зависимостей (DI) - привязка интерфейса к вашей реализации, только контейнер знает об этих привязках.Вы вместо этого кодируете против интерфейсов.Это приносит массу преимуществ.Больше, чем я могу упомянуть здесь.
В любом случае, это также исключает статические классы (так как это будет зависеть от конкретного класса).Вместо этого я проинструктирую контейнер (в данном случае Ninject) всегда передавать мне один и тот же экземпляр для фабричного интерфейса в одноэлементном поведении.
Это исключает опцию public static class Factory<T>, потому что она статическая, и еслиэто не статично, мне нужно было бы теперь использовать каждый тип T, который я собираюсь использовать, что несколько ослабляет цель иметь универсальный класс.
Предложение иметь неуниверсальный класс с "полностью универсальным" методом (какесли я передаю ServiceCall<MyResult> вместо MyResult), то, что я делаю сейчас более или менее (за исключением части статического класса).Контейнер Ninject имеет метод Get, который работает как ваше второе предложение.
Проблема с этим состоит из двух частей;во-первых, это делает мой код напрямую зависимым от одного контейнера (Ninject), но это не такая уж большая проблема для меня.Что меня раздражает, так это то, что если вы посмотрите на меня, Клиент, со стороны, вы увидите только зависимость от Ninject.Вы не будете знать, пока не попробуете сделать вызов, что клиенту нужна реализация ServiceCall, зарегистрированная в Ninject, для работы.
Но если бы клиентский конструктор принял параметр типа Factory>, то это было бы намного понятнее.

В любом случае я бы подумал, что это будет общая проблема, поэтому либо есть общее решение, либо это не общая проблема, и я пытаюсь сделать что-то глупое;) Я все еще не настолько увлечен внедрением зависимости, так чтовполне может быть так.

Ответы [ 2 ]

4 голосов
/ 26 сентября 2010

Это то, что вы ищете: Общий шаблон фабрики

namespace GenericFactoryPatternTestApp
{
    public class Factory< T >
    {
        private readonly Dictionary< string, Type > _factoryDictionary = new Dictionary< string, Type >();

        public Factory()
        {    
            Type[] types = Assembly.GetAssembly(typeof (T)).GetTypes();

            foreach (Type type in types)
            {
                if (!typeof (T).IsAssignableFrom(type) || type == typeof (T))
                {
                    // Incorrect type
                    continue;
                }

                // Add the type
                _factoryDictionary.Add(type.Name, type);
            }
        }

        public T Create< V >(params object[] args)
        {
            return (T) Activator.CreateInstance(_factoryDictionary[typeof (V).Name], args);
        }
    }
}
1 голос
/ 02 августа 2010

Так что, если я вас правильно понимаю, у вас будет класс, похожий на этот:

public static class Factory<T<>>
{
    public static T<TU> CreateInstance<TU>() { /* ... */ }
}

, а затем вы назовете его как?Как это?

var myServiceCall = Factory<ServiceCall<>>.CreateInstance<MyResult>();

Что мешает вам просто объявить фабрику вот так ...

public static class Factory<T>
{
    public static T CreateInstance() { /* ... */ }
}

... или это ...

public static class Factory
{
    public static T CreateInstance<T>() { /* ... */ }
}

... а потом генерируете свой экземпляр вот так?

var myServiceCall = Factory<ServiceCall<MyResult>>.CreateInstance();
var myServiceCall = Factory.CreateInstance<ServiceCall<MyResult>>();

Извините, если я тупой, но мне, вероятно, нужно знать, как работает Ninject и как он позволяет вам проходитьна фабрике.

...