Внедрение в конструктор и перегрузки по умолчанию - PullRequest
6 голосов
/ 19 ноября 2008

Допустим, у нас есть

public interface ITimestampProvider
{
    DateTime GetTimestamp();
}

и класс, который его потребляет

public class Timestamped
{
    private ITimestampProvider _timestampProvider

    public Timestamped(ITimestampProvider timestampProvider)
    {
        // arg null check

        _timestampProvider = timestampProvider;
    }

    public DateTime Timestamp { get; private set; }

    public void Stamp()
    {
        this.Timestamp = _timestampProvider.GetTimestamp();
    }
}

и реализация по умолчанию:

public sealed class SystemTimestampProvider : ITimestampProvider
{
    public DateTime GetTimestamp()
    {
        return DateTime.Now;
    }
}

Полезно или вредно вводить этот конструктор?

public Timestamped() : this(new SystemTimestampProvider())
{}

Это общий вопрос, т. Е. Временная метка не самая интересная часть.

Ответы [ 6 ]

7 голосов
/ 19 ноября 2008

Я думаю, что это зависит от сценария и в основном зависит от того, кто является потребителем кода (библиотека или приложение) и используете ли вы контейнер IoC или нет.

  • Если вы используете контейнер IoC, и он не является частью общедоступного API, тогда позвольте контейнеру выполнить тяжелую работу, и у вас будет только один конструктор. Добавление конструктора без аргументов только запутывает, так как вы никогда не будете его использовать.

  • Если это часть общедоступного API, сохраните оба. Если вы используете IoC, просто убедитесь, что ваш IoC находит «самый жадный» конструктор (тот, у которого больше аргументов). Люди, не использующие IoC, но использующие ваш API, оценят необходимость создания целого графа зависимостей для использования вашего объекта.

  • Если вы не используете контейнер IoC, а просто хотите провести модульное тестирование с макетом, сохраните конструктор no-args и сделайте внутренний конструктор жадным. Добавьте InternalsVisibleTo для вашей сборки модульного теста, чтобы он мог использовать жадный конструктор. Если вы просто юнит-тестирование, вам не нужна дополнительная публичная поверхность API.

4 голосов
/ 19 ноября 2008

я бы не предоставил этот конструктор. Это делает слишком простым вызов нового TimeStamped и получение экземпляра с новым SystemTimestampProvider (), когда ваш IoC может быть настроен на использование OtherTimestampProvider ().

В конце дня вы в один прекрасный момент попытаетесь отладить, почему вы получаете неправильную метку времени.

Если вы предоставляете только первый конструктор, вы можете просто найти использование SystemTimestampProvider, чтобы выяснить, кто (неправильно) использует этот поставщик вместо настроенного поставщика IoC.

3 голосов
/ 19 ноября 2008

В общем, я так не думаю ... Это зависит от того, для чего вы используете Dependency Injection. Когда я использую DI для модульного тестирования, я делаю то же самое (более или менее), создавая экземпляр производственной версии зависимого объекта, когда внедренный экземпляр имеет значение NULL ... И тогда у меня возникает перегрузка, которая не принимает параметр и делегирует к тому, который делает ... Я использую один без параметров для производственного кода, и внедряю тестовую версию для методов модульного тестирования ...

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

   public class EventsLogic
   { 
       private readonly IEventDAL ievtDal;
       public IEventDAL IEventDAL { get { return ievtDal; } }

       public EventsLogic(): this(null) {}
       public EventsLogic(IIEEWSDAL wsDal, IEventDAL evtDal)
       {
          ievtDal = evtDal ?? new EventDAL();
       }
    }
0 голосов
/ 19 ноября 2008

Моя команда имеет большой успех, используя этот метод. Я рекомендую одно изменение:
Сделать _timestampProvider только для чтения. Это вынуждает поставщика быть детерминированным при создании и устранять ошибки.

public class Timestamped
{
    private readonly ITimestampProvider _timestampProvider;

    public Timestamped(ITimestampProvider timestampProvider)
    {
        _timestampProvider = timestampProvider;
    }

    public Timestamped(): this(new SystemTimestampProvider())
    { }
}

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

0 голосов
/ 19 ноября 2008

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

Другой вариант - реализовать метод получения, возвращающий реализацию по умолчанию:

public DateTime Timestamp
{
    get { return _timestampProvider??new SystemTimestampProvider(); }
    set { _timestampProvider = value; }
}

В качестве альтернативы, вы можете реализовать вышеизложенное с помощью синглтона, если вас беспокоит создание слишком большого количества объектов в куче.

0 голосов
/ 19 ноября 2008

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

Потребность в вводимых объектах по умолчанию значительно снижается благодаря использованию контейнера внедрения зависимостей (я использую StructureMap) для управления всей этой проводкой - контейнер DI гарантирует, что вы всегда получаете конкретный экземпляр, который можете использовать.

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

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

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