2008-11-18 3 views
6

Допустим, мы имеемКонструкторы нагнетательных и по умолчанию перегрузках

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; 
    } 
} 

это полезно или harfmful ввести этот конструктор?

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

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

+0

Какой вкус инъекции зависимости вы используете CAB? Замок? – 2008-11-18 22:00:52

+0

Для целей этого вопроса нет. Это общий запрос API. Я обновил вопрос, чтобы удалить коннотацию «инъекции». – 2008-11-18 22:09:34

ответ

7

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

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

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

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

4

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

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

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

+0

Хотя IoC не является частью сценария здесь, я предполагаю, что большинство реализаций будут вводить провайдера, если он зарегистрирован, в противном случае использовать конструктор по умолчанию, то есть вы получите только «SystemTimestampProvider», если вы не переопределили «ITimestampProvider», что является точным намерение. Мысли? – 2008-11-22 02:30:52

3

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

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

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

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

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

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

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

0

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

Другим вариантом было бы реализовать поглотитель, который возвращает реализацию по умолчанию:

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

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

0

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

public class Timestamped 
{ 
    private readonly ITimestampProvider _timestampProvider; 

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

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

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