Как внедрить зависимости в классы, которые реализуют интерфейс? - PullRequest
6 голосов
/ 07 ноября 2010

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

Ответы [ 5 ]

7 голосов
/ 07 ноября 2010

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

public interface IBlogRepository
{
    IEnumerable<Entry> GetEntries(int pageId, int pageCount);
}

class BlogDatabase : IBlogRepository
{
    public BlogDatabase(ISession session)
    {
        this.session = session;
    }

    public IEnumerable<Entry> GetEntries(int pageId, int pageCount)
    {
        // Not that you should implement your queries this way...
        var query = session.CreateQuery("from BlogEntry");
        return query.Skip(pageId * pageCount).Take(pageCount);
    }

    private ISession session;
}

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

public interface IBlogRepository
{
    ISession Session { get; set; }
    IEnumerable<Entry> GetEntries(int pageId, int pageCount);
    IEnumerable<Entry> GetEntriesWithSession(ISession session,
        int pageId, int pageCount);
}

class BlogDatabase : IBlogRepository
{
    public ISession Session { Get; set; }

    public IEnumerable<Entry> GetEntries(int pageId, int pageCount)
    {
        var query = Session.CreateQuery ...
    }

    public IEnumerable<Entry> GetEntries(ISession session, int pageId, int pageCount)
    {
        var query = session.CreateQuery ...
    }
}

class BlogFile : IBlogRepository
{
    // ISession has to abstract a file handle.  We're still okay
    // ...
}

class BlogInMemory : IBlogRepository
{
    // ISession abstracts nothing.
    // Maybe a lock, at best, but the abstraction is still breaking down
    // ...
}

Внедрение в конструктор будет работать только в том случае, если вы используете какую-то платформу Dependency Injection, которая может обрабатывать создание / предоставление зависимостей длявы.Внедрение свойств и аргументов будет работать даже без рамок.

Я считаю, что все три являются принятой практикой.По крайней мере, пара популярных фреймворков поддерживают внедрение как конструктора, так и свойства.

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

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

public interface IInstallationFactory
{
    IUser CreateRegisteredUser(Guid userSid);
    IPackage CreateKnownPackage(Guid id);
    IInstaller CreateInstaller();
}

Различные инфраструктуры также поддерживают абстрактные фабрики.

3 голосов
/ 07 ноября 2010

Один из вариантов - создать метод интерфейса для инициализации.Этот метод может принять все необходимые зависимости.

Что-то вроде:

void Configure(dependency1 value, etc.);

Конечно, существует множество вариантов выполнения этого типа инициализации и DI с использованием инфраструктуры.Хотя есть из чего выбирать.

Скотт Хансельман имеет хороший список здесь .

2 голосов
/ 07 ноября 2010

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

на первый взгляд это может показаться странным, но мы постоянно используем его в наших корпоративных решениях, поэтому я гарантирую, что это нормально: -)

0 голосов
/ 08 ноября 2017

Интерфейс не отвечает за зависимости. Только реализация знает, что нужно для выполнения контракта.

Может быть одна реализация, использующая базу данных, другая - файловая система для сохранения данных.

Какую зависимость интерфейс должен объявить требуемой? Менеджер базы данных или менеджер файловой системы?

0 голосов
/ 09 ноября 2010

Мы все знаем, что это возможно разными способами, но что-то, что имеет смысл, несомненно, приветствуется.Я определил некоторые set-only свойства, затем объект отвечает за хранение ссылки на то, что ему передается:

public interface IBlogRepository
{
    ISession Session { set; }
}

class BlogRepository : IBlogRepository
{
   private ISession m_session;

   ISession Session
   {
      set { m_session = value; }
   }
}

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

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