Castle Winsdor - Распространение зависимости, передаваемой через типизированную фабрику - PullRequest
0 голосов
/ 03 сентября 2018

В настоящее время я знакомлюсь с IoC и испытываю трудности с рефакторингом существующей библиотеки с использованием принципов IoC.

Допустим, у меня есть три класса: Контроллер , Обработчик и Настройки и типизированная фабрика для Контроллера.

  • Контроллер является точкой входа в библиотеку

    public class Controller
    {
        public Settings Settings { get; }
        public Handler  Handler  { get; }
    
        public Controller(Settings settings, Handler handler)
        {
            Settings = settings;
            Handler  = handler;
        }
    }
    
  • Обработчик является зависимостью от контроллера

    public class Handler
    {
        public Settings Settings { get; }
    
        public Handler(Settings settings)
        {
            Settings = settings;
        }
    }
    
  • Настройки - это класс, содержащий некоторые общебиблиотечные настройки '

    public class Settings
    {
        public int Revision { get; set; }
    }
    
  • IControllerFactory является типизированной фабрикой

    public interface IControllerFactory
    {
        Controller Create(Settings settings);
    }
    

Я хочу инициализировать библиотеку. Для простоты весь пример кода находится в одном методе Main. В реальных приложениях потребитель класса Controller не имеет доступа к контейнеру.

static void Main(string[] args)
{
    //create container and register components
    var container = new WindsorContainer();
    container.AddFacility<TypedFactoryFacility>();
    container.Register(
        Component.For<Controller>().LifestyleTransient(),
        Component.For<Settings>().LifestyleBoundTo<Controller>(),
        Component.For<Handler>().LifestyleBoundTo<Controller>()
    );
    container.Register(
        Component.For<IControllerFactory>().AsFactory()
    );

    //in real application, factory is a dependency of a library consumer class 
    //which has no access to container
    var controllerFactory = container.Resolve<IControllerFactory>();

    //create Controller instance with Revision setting set to 100
    var settings = new Settings()
    {
        Revision = 100
    };
    var controller = controllerFactory.Create(settings);

    //check revision value for controller and handler
    Console.WriteLine("Controller's setting revision: " + controller.Settings.Revision);         //Controller's setting revision: 100
    Console.WriteLine("Handler's setting revision: "    + controller.Handler.Settings.Revision); //Handler's setting revision: 0
    Console.ReadKey();
}

Выполнение этого примера выдает следующее:

Controller's setting revision: 100
Handler's setting revision: 0

Как вы можете видеть, экземпляр Settings, который передается как аргумент фабрике, правильно передается конструктору Controller, но не распространяется на зависимости Controller (т.е. конструктор Handler). Я не смог найти никакой информации о том, является ли это предполагаемым поведением.


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

static void Main(string[] args)
{
    //create container and register components
    var container = new WindsorContainer();
    container.AddFacility<TypedFactoryFacility>();
    container.Register(
        Component.For<Controller>().LifestyleScoped(),
        Component.For<Settings>().LifestyleScoped(),
        Component.For<Handler>().LifestyleBoundTo<Controller>()
    );

    //creating scope means passing container around
    using (container.BeginScope()) 
    {
        //create instance of controller
        var settings = container.Resolve<Settings>();
        settings.Revision = 100;
        var controller = container.Resolve<Controller>();

        //check revision value for controller and handler
        Console.WriteLine("Controller's setting revision: " + controller.Settings.Revision);         //Controller's setting revision: 100
        Console.WriteLine("Handler's setting revision: "    + controller.Handler.Settings.Revision); //Handler's setting revision: 100
    }

    Console.ReadKey();
}

Выполнение этого примера дает желаемый результат:

Controller's setting revision: 100
Handler's setting revision: 100

1 Ответ

0 голосов
/ 04 сентября 2018

Аргументы, переданные фабрике, не будут добавлены в контейнер - они будут использоваться только для удовлетворения зависимостей требуемого сервиса. Если вы хотите, чтобы эти аргументы были переданы другим зависимостям, вы должны взять на себя ответственность за это самостоятельно.

Общее эмпирическое правило о том, что ссылка на контейнер является плохой практикой, является правильным. Однако, как и все эмпирические правила, у него есть ограничения.

(Фактическая изменчивость Settings не ясна из вашего вопроса, поэтому я предполагаю, что ваше подразумеваемое требование о том, что оно может изменяться неопределенным образом, является ключевым фактором здесь. Однако, отвечая на ваш вопрос о " какой подход вы бы предложили " трудно без правильного понимания реальной изменчивости Settings)

i) С вашим текущим дизайном, так или иначе, вам нужно улучшить поведение контейнера, чтобы выполнить ваши требования, как объяснено, и, следовательно, вы должны получить доступ к контейнеру.

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

ii) Другим подходом было бы немного переосмыслить ваш дизайн и ввести некоторую косвенность в ссылку на Settings. то есть вместо Handler и Controller, имеющих зависимость от Settings, они могут иметь зависимость от SettingsProvider. SettingsProvider затем может быть добавлен в контейнер как единичный объект, и требуемая логика доступа к применяемому в настоящее время Settings может управляться полностью независимо от контейнера.

(См. Это превосходное объяснение преимуществ фабрик в замке Виндзор для получения более подробной информации)

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