autofac / c #: универсальная фабрика для определенного базового класса? - PullRequest
3 голосов
/ 29 мая 2011

Возможно ли для autofac создать универсальную фабрику, которая может разрешать только типы определенного базового класса?

В настоящее время я вижу, насколько возможно модифицировать проект c # brownfield для использования autofac. Проект требует создания дерева компонентов разных типов, но одного базового класса. Этим компонентам требуются службы аутентификации и базы данных (помимо прочего), поэтому сложно просто иметь пустые конструкторы.

Таким образом, фабрика была бы такой, чтобы пользователь мог создавать объекты, порождающие базовый класс, использовать autofac для сканирования этих классов и предоставлять фабрику для них. Я подумал, что это может упростить построение деревьев компонентов, если вам не нужно предоставлять фабрики для каждого класса.

В сторону, или это признак плохого дизайна? Должны ли компоненты и дерево делать как можно меньше, и мы передаем это дерево другим службам для обеспечения обработки и рендеринга?

Что-то вроде (где? Это мистическая фабрика)

public MyBase { 
    public Add(MyBase x) {..}
}

public DerivedA: MyBase {}

public DerivedB : MyBase {}

public DerivedC : DerivedA {}

public SomethingElse
{
     private ? _myBaseFact;

     public SomethingElse(? myBaseFact) { 
            _myBaseFact = myBaseFact;
     }

     public BuildComponentTree() {
        DerivedA a = _myBaseFact<DerivedA>();
        DerivedB b = _myBaseFact<DerivedB>();
        DerivedC c = _myBaseFact<DerivedC>();
        a.Add(b);
        b.Add(c);
     }
}

edit: меня попросили дать более конкретный пример, который является справедливым :) Есть компоненты фреймворка, и я хотел позволить разработчикам создавать свои собственные компоненты без необходимости регистрации в autofac. Наличие фабричного класса будет означать, что разработчик должен будет добавить свои пользовательские компоненты к этой фабрике, верно? Пожалуйста, извините за пример, это похоже на то, как работает фреймворк, когда я с ним столкнулся.

FwComponent {
    public FwComponent (DataProvider, AuthManager, OtherModule)
    public Add(FwComponent x);
    public Setup();
    public Process();
    public Render();
}

Page : FwComponent {
    public Page(DataProvider, AuthManager, OtherModule): base(..)
    public string Title;
    ...
}

Input: FwComponent {
    public Input(DataProvider, AuthManager, OtherModule): base(..)
    public string Name;
    public int length;
    ...
}

Button: FwComponent {
    public Button(DataProvider, AuthManager, OtherModule): base(..)
    public string Name;
    ...
}

----

MyCustomButton : Button {
     public MyCustomButton(DataProvider, AuthManager, OtherModule): base(..)
}

MyPersonalPage : FwComponent {

    IContext _container;

    public MyPersonalPage(DataProvider, AuthManager, OtherModule, IContext container): base(..)

    public Setup() {
         var input = _container.Resolve<Input>();
         input.Name = 'xxx';
         input.length = 10;
         Add(input);

         var butt = _container.Resolve<MyCustomButton>();
         butt.name = "xxx";
         Add(butt);    
    }

    public Process() {
        DataProvider.DoStuff();
    }
}

Ответы [ 2 ]

5 голосов
/ 29 мая 2011

Как я уже упоминал в своем комментарии к ответу Стивена, ваш вопрос, похоже, основан на неправильном понимании цели фабричного паттерна и полиморфизма. Я оставлю это его ответу, но давайте вернемся к вашему неправильному пониманию внедрения зависимости ...

1) Внедрение зависимостей предназначено для служб, а не для организаций

Не ясно, каковы ваши компоненты и дерево, но я предполагаю, что они являются объектами в вашем домене, а не службами, которые обрабатывают их. Внедрение зависимостей предназначено для внедрения услуг (в самом общем смысле) в другие службы или слои клея. Примеры включают внедрение репозитория в контроллер MVC или размещение веб-службы в Твиттере в модели представления или фабрику продуктов в службу манипулирования продуктами.

2) Внедрение зависимостей и их каркасы не имеют ничего общего с заводским шаблоном

Хотя он называется AutoFac , он не является своего рода auto matic fac тори-генератором, так как Unity - это способ объединить все ваши занятия в одном или Виндзорский замок это ... ну ... я понятия не имею. Единственное соединение, которое могут иметь эти две концепции, - это если фабрика является одной из вышеупомянутых служб, которые вы внедряете.

3) Внедрение зависимостей зависит от хорошего дизайна

То есть вы должны использовать надлежащие объектно-ориентированные соображения, например, использовать конструктор для построения класса. Хотя я подозреваю, что компоненты в вашем дереве являются сущностями (как обсуждалось выше), если бы они были сервисами, то метод Add противоречил бы этим принципам проектирования: если классу нужен экземпляр MyBase, он должен принять это как параметр конструктора с соответствующим условным выражением для выброса исключения, если этот инвариант не выполняется. Точно так же, SomethingElse, очевидно, нужно дерево компонентов, которое может быть задано корневым узлом: ну, тогда он должен принять этот корневой узел в качестве параметра конструктора.


Другими словами, дружественная к внедрению зависимостей версия древовидной ситуации в конечном итоге выглядела бы как

interface INotificationService { }
class TwitterNotificationService : INotificationService { }
class FacebookNotificationService : INotificationService { }
class CompositeNotificationService : INotificationService
{
    public CompositeNotificationService(IEnumerable<NotificationService> services) { }
    // implement composite pattern
}

class NotificationController : Controller
{
    public NotificationController(INotificationService service)
    {
        if (service === null)
        {
            throw new ArgumentNullException("service");
        }

        // ...
    }
}

Позже, если бы вы использовали DI бедняка, вы бы сделали что-то вроде

var controller = new NotificationController(
        new CompositeNotificationService(new []
        {
            new TwitterNotificationService(),
            new FacebookNotificationService()
        }));

Цель DI-фреймворка состоит в том, чтобы просто переместить код конструкции шаблона, подобный этому, в одно центральное место в вашем приложении ( составной корень ), эффективно удаляя конструкцию службы как проблему из вашего приложения и превращая это в детали конфигурации. Но ваше приложение должно быть совместимо с DI, что на самом деле просто означает соответствие базовым принципам ОО, таким как использование конструкторов, для работы DI.

2 голосов
/ 29 мая 2011

В приведенном вами примере кода вы, похоже, запрашиваете определенный тип, используя _myBaseFact<DerivedA>() и _myBaseFact<DerivedB>(). Трудно понять, чего вы пытаетесь достичь, но мне кажется, что это пахнет кодом, потому что у вас все еще есть зависимость от конкретных типов.

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

1: У вас есть какой-то (не основанный на типе) аргумент, который позволяет фабрике идентифицировать возвращаемый тип, как показано в следующем примере:

public class MyBaseFactory : IMyBaseFactory
{
    public MyBase CreateMyBase(IUserContext user)
    {
        // Create a MyBase based on a user object.
    }
}

2: на вашем заводе есть несколько методов:

public class MyBaseFactory : IMyBaseFactory
{
    private Autofac.IContainer container;

    public MyBase CreateMyBaseForScenarioA()
    {
        return container.Resolve<DerivedA>();
    }

    public MyBase CreateMyBaseForScenarioB()
    {
        return container.Resolve<DerivedB>();
    }
}

Обратите внимание, что оба заводских метода возвращают MyBase, а не конкретный тип. Вся идея полиморфизма заключается в том, что вам не нужно заботиться о конкретном типе.

IMO не имеет значения, может ли Autofac создать фабрику для вас или нет. Вы не должны полагаться на такие специфические для фреймворка конструкции, а зависеть от хорошего дизайна приложения. Если вы используете фабричный подход, определите интерфейс для этой фабрики и внедрите экземпляр на основе этого интерфейса. Это делает дизайн очень чистым и очень красиво передает его.

...