Параметры конструктора для зависимых классов с Unity Framework - PullRequest
3 голосов
/ 20 марта 2010

Я только начал использовать Unity Application Block, чтобы отделить мои классы и облегчить юнит-тестирование. Однако я столкнулся с проблемой циклических зависимостей.

У меня есть класс типа фасада, который является чат-ботом. Это одноэлементный класс, который обрабатывает все виды вторичных классов и обеспечивает центральное место для запуска и настройки бота. У меня также есть класс AccessManager, который, в общем-то, управляет доступом к командам и ресурсам бота. Суть сводится к тому, что у меня настроены классы так:

public class Bot
{
    public string Owner { get; private set; }
    public string WorkingDirectory { get; private set; }

    private IAccessManager AccessManager;

    private Bot()
    {
       // do some setup

       // LoadConfig sets the Owner & WorkingDirectory variables
       LoadConfig();

       // init the access mmanager
       AccessManager = new MyAccessManager(this);
    }

    public static Bot Instance()
    {
       // singleton code
    }

    ...
}

И класс AccessManager:

public class MyAccessManager : IAccessManager
{
    private Bot botReference;

    public MyAccesManager(Bot botReference)
    {
        this.botReference = botReference;
        SetOwnerAccess(botReference.Owner);
    }

    private void LoadConfig()
    {
        string configPath = Path.Combine(
            botReference.WorkingDirectory, 
            "access.config");
        // do stuff to read from config file
    }

    ...
}

Я хотел бы изменить этот дизайн для использования блока приложений Unity. Я хотел бы использовать Unity для генерации одиночного бота и загрузки интерфейса AccessManager в каком-то методе начальной загрузки, который выполняется раньше, чем что-либо еще.

public static void BootStrapSystem()
{
   IUnityContainer container = new UnityContainer();

   // create new bot instance
   Bot newBot = Bot.Instance();

   // register bot instance 
   container.RegisterInstance<Bot>(newBot);

   // register access manager
   container.RegisterType<IAccessManager,MyAccessManager>(newBot);
}

И когда я хочу получить ссылку на Access Manager внутри конструктора Bot, я могу просто сделать:

IAcessManager accessManager = container.Resolve<IAccessManager>();

И в других местах системы, чтобы получить ссылку на синглота Бота:

// do this
Bot botInstance = container.Resolve<Bot>();
// instead of this
Bot botInstance = Bot.Instance();

Проблема в том, что метод BootStrapSystem() взорвется. Когда я создаю экземпляр бота, он попытается разрешить IAccessManager, но не сможет, потому что я еще не зарегистрировал типы (это следующая строка). Но я не могу перенести регистрацию перед созданием бота, потому что в рамках регистрации мне нужно передать бота в качестве параметра! Круговые зависимости !! Г !!!

Это указывает на то, что у меня есть недостаток в том, как я это структурировал. Но как мне это исправить? Помогите !!

Ответы [ 2 ]

1 голос
/ 21 марта 2010

Вы можете упростить свою жизнь, изменив дизайн следующими способами:

  • Не реализуйте шаблон Singleton самостоятельно.Контейнер DI должен управлять временем жизни всех компонентов, включая класс Bot.Если вы хотите, чтобы в вашем приложении был только один экземпляр, сконфигурируйте Unity так, чтобы он всегда возвращал один и тот же экземпляр.
  • Сделайте все от вас зависящее, чтобы удалило циклические зависимости .Часто это можно сделать, изменив одно из направлений связи, чтобы использовать события вместо прямых вызовов.Другой вариант - ввести Mediator .

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

1 голос
/ 21 марта 2010

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

container.RegisterType<Bot>(new ContainerControlledLifecycleManager()); // from my memory...
container.RegisterType<IAccessManager,MyAccessManager>();

var bot = container.Resolve<Bot>();

// Bot.cs
public Bot(IAccessManager manager)
{
   manager.InitializeFor(this);
}

Из соображений тестируемости никогда не следует вызывать свой контейнер IOC из конструктора.

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