как IoC это? - PullRequest
       50

как IoC это?

1 голос
/ 21 февраля 2011

Я хочу посмотреть, как Ioc / Di может упростить подключение следующих часто используемых классов:

Рассмотрим библиотеку, в которой есть абстрактное понятие сущности и интерфейс для объекта доступа к данным:

public abstract class EntityWithTypedId<TId> : IEntityWithTypedId<TId>{...}

public interface IDao<T, TId> where T : IEntityWithTypedId<TId>

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

// has an NHib implementation
public class Dao<T, TId> : IDao<T, TId> where T : EntityWithTypedId<TId> {...}

public class DaoFakeBase<T> : IDao<T, int>, IDisposable where T : IEntityWithTypedId<int> {...}

В настоящее время я делаю следующее, чтобы определить тип сущности и даодля данного проекта:

/// <summary>
/// <see cref="IEntityWithTypedId{IdT}"/> with an int for an id
/// </summary>
[Serializable]
public abstract class Entity : EntityWithTypedId<int>
{
}

public class Dao<T> : Dao<T, int> where T : Entity
{
    protected Dao(ISessionFactory sessionFactory) : base(sessionFactory) { }

}

Могу ли я использовать инструмент DI для определения объекта?Может кто-нибудь показать мне пример кода, как это сделать, если так?

Не могли бы вы также изложить, как я могу указать моей тестовой сборке использовать DaoFakes и production для использования NHib.Dao

Я смотрел на Виндзор, главным образом потому, что его используют в проектах, разработанных NHibernate, ноЯ также заинтересован в MEF, AutoFac и Ninject, в этом порядке.Я понимаю, что MEF не является контейнером IoC в том смысле, как это делает Виндзор.Из того, что я вижу в Windsor, я бы использовал классы Installer, может быть EntityInstaller и DaoInstaller, хотя я мог бы и здесь пропустить объект типа FActory.

Cheers,
Berryl

ОБНОВЛЕНИЕ @ KeithS

Вы говорите, чтобы изменить что-то вроде:

 class MyViewModel(IDao<MyClass, int> dao) {...}

становится что-то вроде

class MyViewModel(Func<IDao<MyClass, int>, obj> getDaoFunc) {
    _dao = getDaoFunc(this);
}   

Ответы [ 2 ]

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

В вашем примере ...

class MyViewModel(IDao<MyClass, int> dao) {...}

... IDao будет решаться во время выполнения на основе предыдущей регистрации в вашем контейнере.Синтаксис для реализации Prism / Unity приведен ниже ...

IUnityContainer.RegisterType<IDao..., DaoFakeBase...>();

RegisterType происходит в IModule.Initialize () данного модуля, как определено в классе UnityBootstrapper.

protected override IModuleCatalog GetModuleCatalog()
{
    ModuleCatalog catalog = new ModuleCatalog();
    catalog.AddModule(typeof(project.Init));
    return catalog;
}

Вы также можете зарегистрировать данный тип на основе пожизненного менеджера;вести себя как синглтон ...

IUnityContainer.RegisterType<IShellController, ShellController>(new ContainerControlledLifetimeManager());

... где разрешенный экземпляр IShellController будет оставаться тем же возвращаемым экземпляром в течение всего времени жизни IUnityContainer.

ОБНОВЛЕНИЕ:

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

    public interface IDao<T, TId> where T : IEntityWithTypedId<TId>
    { }

    public class Dao<T, TId> : IDao<T, TId> where T : EntityWithTypedId<TId> 
    { }

    public class TId
    { }

    public abstract class EntityWithTypedId<TId> : IEntityWithTypedId<TId>
    { }

    public interface IEntityWithTypedId<TId>
    { }

    IUnityContainer.RegisterType<IEntityWithTypedId<TId>, EntityWithTypedId<TId>>();
    IUnityContainer.RegisterType<IDao<IEntityWithTypedId<TId>, TId>, Dao<IEntityWithTypedId<TId>, TId>>();

    IDao<IEntityWithTypedId<TId>, TId> dao = IUnityContainer.Resolve<IDao<IEntityWithTypedId<TId>, TId>>();
1 голос
/ 21 февраля 2011

Я бы не использовал IoC для регистрации отношений между DAO и их типами (что, в сущности, вы и делаете). Это приведет к тому, что вы будете использовать контейнер IoC в качестве «локатора службы», известного анти-паттерна, когда вы передаете контейнер IoC в объекты, которые будут использовать его для получения необходимого DAO.

Я думаю, что лучший способ упростить это с точки зрения потребления - это определить шаблон стратегии, используя фабричный класс или метод:

public Dao<T, TId> GetDaoFor<T, TId>(T objectInstance) where T:EntityWithTypedId<TId>
{
    //Here, you could use a Dictionary, Linq with some reflection, etc.
}

Этот один метод может быть внедрен в качестве делегата в классы, зависящие от DAO. Разница в том, что классы, которым нужен DAO, зависят от метода, который может им их предоставить, который может быть предоставлен контейнером IoC; они НЕ зависят от самого контейнера (который является основным источником зла, присущим шаблону «поиск сервисов»). Это уменьшает количество вещей, которые вам придется изменить, если вы переписали, как вы получили эти DAO.

РЕДАКТИРОВАТЬ: Немного не по теме, но я открыл дверь:

Шаблон местоположения службы, как правило, следует избегать, поскольку он приводит к коду, который опирается на указатель службы. Например, в коде часто встречается следующее, где IoC был выставлен на дочерних уровнях:

private IDependency _dependency;
public IDependency MyDependency
{
   get {
         _dependency = _dependency ?? IoC.Resolve<IDependency>();
         return _dependency;
       }
}

Хотя это выглядит как хороший шаблон (зависимости инициализируются лениво, потребляющему коду не нужно знать о зависимостях дочернего элемента, и вы всегда * получаете ссылку), этот код ВСЕГДА будет требовать наличия синглтона IoC. Вы можете изменить структуру IoC, стоящую за ней, вы можете полностью удалить сторонний инструмент и свернуть свой собственный, но этот класс всегда будет требовать что-то, для чего статически вызывать Resolve<IDependency>().

Вы также НЕ ВСЕГДА получаете ссылку; Вы получите ссылку, только если вы правильно зарегистрировали IDependency в IoC. Это создает еще две слабости; 1) вы не знаете, что понадобится классу, не открывая его, и 2) если / когда вызов не удастся, он потерпит неудачу глубоко в недрах внутренней работы зависимого класса. Если вы разрабатываете новый класс и подключаете его к IoC, он может пройти интеграцию и даже некоторое время работать в производственной среде, пока не начнете получать странные ошибки «объектная ссылка установлена ​​на ноль» в действительно странных местах кода, которые поверь мне, кошмар для отладки.

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

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

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