Имеется .NET приложение, использующее Unity. Возникла необходимость в фабрике объектов — что-то вроде "Абстрактной фабрики" в терминологии GoF.
Конструкторы объектов, которые должна создавать фабрика, можно разделить на категории:
1) "глобальные" сервисы, доступны через IoC-контейнер
2) конфигурационные параметры, их значения определяются при создании фабрики и не должны быть видимы использующему фабрику коду
3) параметры, указываемые при создании объекта, должны быть переданы в метод фабрики
Как реализовать подобную фабрику?
Возможно, у данной задачи есть другое решение?
Покажу псевдокодом.
// Интерфейс фабрикиpublic interface IFactory<T>
{
// Методы для создания и конфигурирования фабрики
}
// Пример объекта, который должна создавать фабрикаpublic sealed class Foo : IFoo
{
public Foo(
IService service, // Значение этого параметра нужно получать из Unity
String param, // Значение этого параметра узнаём при конфигурации фабрики
Context context) // Значение этого параметра становится известно только перед созданием объекта
{
...
}
}
// Использование фабрики, в каком-либо классеpublic class Bar
{
public IFactory<IFoo> Factory {get; set};
public void SomeMethod()
{
Context fooContext = ...; // Значение третьего параметра
IFoo foo = this.fooFactory.????? // Код создания объекта
...
}
}
// Создание и конфигурирование фабрики, при запуске приложения
String paramValue = ...; // Значение второго параметра
Factory<IFoo> factory = new Factory<IFoo>(???); // Код создания и
??????? // конфигурирования фабрики
Bar bar = ...;
bar.FooFactory = factory;
Здравствуйте, Poul_Ko, Вы писали:
P_K>Привет всем.
P_K>Как реализовать подобную фабрику? P_K>Возможно, у данной задачи есть другое решение?
посмотрите на наследников InjectionMember
Один из них вам должен подойти (InjectionFactory или InjectionConstructor)
Здравствуйте, Poul_Ko, Вы писали:
P_K>Привет всем.
P_K>Имеется .NET приложение, использующее Unity. Возникла необходимость в фабрике объектов — что-то вроде "Абстрактной фабрики" в терминологии GoF. P_K>Конструкторы объектов, которые должна создавать фабрика, можно разделить на категории: P_K>1) "глобальные" сервисы, доступны через IoC-контейнер P_K>2) конфигурационные параметры, их значения определяются при создании фабрики и не должны быть видимы использующему фабрику коду P_K>3) параметры, указываемые при создании объекта, должны быть переданы в метод фабрики
P_K>Как реализовать подобную фабрику? P_K>Возможно, у данной задачи есть другое решение?
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, Poul_Ko, Вы писали:
P_K>>Привет всем.
P_K>>Имеется .NET приложение, использующее Unity. Возникла необходимость в фабрике объектов — что-то вроде "Абстрактной фабрики" в терминологии GoF. P_K>>Конструкторы объектов, которые должна создавать фабрика, можно разделить на категории: P_K>>1) "глобальные" сервисы, доступны через IoC-контейнер P_K>>2) конфигурационные параметры, их значения определяются при создании фабрики и не должны быть видимы использующему фабрику коду P_K>>3) параметры, указываемые при создании объекта, должны быть переданы в метод фабрики
P_K>>Как реализовать подобную фабрику? P_K>>Возможно, у данной задачи есть другое решение?
G>А для чего тебе это все нужно?
Я описал задачу абстрактно потому, что она встречается в нескольких местах. Везде суть одна — нужно создавать объекты, параметры создания которых могут определяться глобально, конфигурацией и контекстом использования.
Давайте возьмём конкретную ситуацию. Пусть разрабатывается "богатое" клиентское приложение на WinForms. Одной из важных частей функциональности является табличное отображение данных. Пусть за отображение данных в одном столбце грида отвечает интерфейс IColumnController.
Тогда модуль, который отображает данные в табличном виде, среди прочего должен оперировать экземплярами IColumnController. Идея в том, чтобы передать в этот модуль при его конфигурировании набор фабрик IColumnController'ов (по одной на каждый столбец). В нужное время модуль создаст объекты IColumnController через переданные фабрики.
Конкретные классы, реализующие IColumnController, могут зависеть от каких угодно сервисов, имеющихся в приложении, в том числе и не существующих на момент проектирования. Эти зависимости логично разрешать через Unity. Примером конфигурационной информации для ColumnController может быть формат вывода значения (не имеет смысла делать отдельный класс под каждый формат, проще передать в экземпляр строку формата).
Здравствуйте, Poul_Ko, Вы писали:
P_K>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, Poul_Ko, Вы писали:
P_K>>>Привет всем.
P_K>>>Имеется .NET приложение, использующее Unity. Возникла необходимость в фабрике объектов — что-то вроде "Абстрактной фабрики" в терминологии GoF. P_K>>>Конструкторы объектов, которые должна создавать фабрика, можно разделить на категории: P_K>>>1) "глобальные" сервисы, доступны через IoC-контейнер P_K>>>2) конфигурационные параметры, их значения определяются при создании фабрики и не должны быть видимы использующему фабрику коду P_K>>>3) параметры, указываемые при создании объекта, должны быть переданы в метод фабрики
P_K>>>Как реализовать подобную фабрику? P_K>>>Возможно, у данной задачи есть другое решение?
G>>А для чего тебе это все нужно?
P_K>Я описал задачу абстрактно потому, что она встречается в нескольких местах. Везде суть одна — нужно создавать объекты, параметры создания которых могут определяться глобально, конфигурацией и контекстом использования.
Параметры можно помещать в сам контейнер, для контекста использования создавать дочерние контейнеры. Кроме того можно тупо возвращать функции + factory расширение, которое позволит генерировать функцию нужного вида.
Здравствуйте, gandjustas, Вы писали:
G>Параметры можно помещать в сам контейнер, для контекста использования создавать дочерние контейнеры. Кроме того можно тупо возвращать функции + factory расширение, которое позволит генерировать функцию нужного вида.
Это и было моей первой реализацией.
Напрягает то, что при помещении параметра в контейнер не проверяется, имеет ли конструктор класса такой параметр. Получается, что пока мы не попытаемся создать объект мы не узнаем, что параметр был задан неправильно (или не задан, или значение имеет неподходящий тип...). Хотелось бы такие ошибки отлавливать, например, при регистрации типа.
Здравствуйте, Poul_Ko, Вы писали:
P_K>Здравствуйте, gandjustas, Вы писали:
G>>Параметры можно помещать в сам контейнер, для контекста использования создавать дочерние контейнеры. Кроме того можно тупо возвращать функции + factory расширение, которое позволит генерировать функцию нужного вида.
P_K>Это и было моей первой реализацией. P_K>Напрягает то, что при помещении параметра в контейнер не проверяется, имеет ли конструктор класса такой параметр. Получается, что пока мы не попытаемся создать объект мы не узнаем, что параметр был задан неправильно (или не задан, или значение имеет неподходящий тип...). Хотелось бы такие ошибки отлавливать, например, при регистрации типа.
Сделай надстройку над контейнером, валидирующую параметры. Будет что-то вроде
var childContainer = baseContainer.InitilizeContext(parameters);
Здравствуйте, gandjustas, Вы писали:
G>Сделай надстройку над контейнером, валидирующую параметры. Будет что-то вроде
Спасибо, поизучаю вопрос в этом направлении.
Сейчас остановился на использовании Resolve(Type, ResolverOverride[]), это не требует создания дочернего контейнера.
Вообще, меня больше интересует как подобные архитектурные проблемы решают другие разработчики. Возможно как-то можно обойтись без фабрик? Создавать все объекты заранее и хранить уже созданные? Использовать service lookup? Какие ещё есть варианты?
Здравствуйте, Poul_Ko, Вы писали:
P_K>Какие ещё есть варианты?
Вроде что-то похожее есть в Guice.
Как я понял, получится что-то типа этого:
Твои интерфейсы и имплементация:
public interface IFoo
{
...
}
public interface IFooFactory
{
IFoo create(Context context);
}
public class Foo : IFoo
{
@Inject
Foo(
IService service,
String param,
... // можно сколь угодно много разных зависимостей добавить.
@Assisted Context context) // @Assisted параметры должны соотвествовать методу create фабрики, можно делать несколько.
{
...
}
}