Типовые ограничения на конструктор для фабричного метода - PullRequest
1 голос
/ 21 февраля 2012

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

Итак, мой случай:

Я пишу фабричный метод, для классов ведьма была получена из моего специальногоБазовый класс.Поэтому я написал

public T MyFactory<T>() where T:MyBaseClass

Но моя основная работа моего фабричного метода - получить некоторые специальные параметры и передать их конструктору нового объекта.MyBaseClass имеет этот конструктор:

public MyBaseClass(MySpecParam param){...}

Но нет никакой гарантии, что тип T, полученный из MyBaseClass, имеет такой конструктор.

Единственное решение, которое я вижу,состоит в том, чтобы добавить new() ограничение и виртуальный Init метод к MyBaseClass, чтобы фабрика смогла создать новый объект типа T, а затем инициировать его с MySpecParam object.

Но у MyBaseClass такой дизайн, поэтому он совершенно непригоден, если не введен с MySpecParam.И пользователь может создать MyBaseClass с помощью конструктора без параметров и получить совершенно недействительный, не инициализированный объект.Я думаю, что это нехорошо.

Нет способа добавить ограничение new(MySpecParam).

Как я должен проектировать свои объекты, конструкторы и метод фабрики?

Ответы [ 3 ]

2 голосов
/ 22 февраля 2012

Звучит как проблема для контейнера IoC .Если ваша фабрика использует такой контейнер, как StructureMap или Unity, проблема с конструктором больше не будет проблемой.Фабрика будет запрашивать StructureMap (например) для разрешения MyBaseClass, и она будет возвращать экземпляр вашего MyBaseClass, используя самый жадный конструктор, рекурсивно создавая все его зависимости и т. Д.

2 голосов
/ 21 февраля 2012

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

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

РЕДАКТИРОВАТЬ

Если вы не желаете использовать рефлексию по причинам, изложеннымв вашем комментарии ответ будет более или менее «нет, вы не можете этого сделать».Michael Yoon предлагает IoC-фреймворк, который, конечно, использует отражение и также подвержен ошибкам во время выполнения.По моему опыту (с Castle Windsor), производительность никогда не была проблемой, и ошибки времени выполнения в результате неправильной конфигурации почти сразу же попадают в цикл разработки.

Другая мысль;это может быть не полезно, но это может стоить рассмотреть.Вы можете использовать Func<T> для создания экземпляра или даже иметь перегрузки фабричного метода для различных типов Func<T, TOut>, Func<T1, T2, TOut> и т. Д., Где вы вызываете

var obj = FactoryMethod<SomeType>(() => new SomeType(23));

В качестве альтернативы рассмотрите абстрактный фабричный шаблон .

1 голос
/ 22 февраля 2012

Конструкторы не наследуются и не допускаются на интерфейсах. Это означает, что конструктор каждого класса специфичен только для этого класса. Это также означает, что подкласс может быть сделан из вашего базового класса, у которого нет конструктора, соответствующего вашему шаблону. Если вы хотите иметь стандартный способ настройки ваших объектов, я думаю, что abstract Init(foo, bar, baz) в вашем базовом классе, как и ваша идея, является лучшим решением. Вы также можете реализовать внутреннюю логику, которая выдает, если к объектам обращаются до их инициализации, но, к сожалению, вы не сможете применить это во время компиляции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...