Насмешливые вопросы и внедрение зависимостей - PullRequest
3 голосов
/ 06 октября 2011

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

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

Ответы [ 4 ]

2 голосов
/ 06 октября 2011

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

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

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

1 голос
/ 06 октября 2011

Позвольте мне пройти пару шагов дальше от hvgotcodes answer :

class Service {
   Collaborator c = new Collaborator()
}

- ваш исходный класс с жестко закодированной зависимостью.

class Service {
    Collaborator c;

    Service(Collaborator c) {
       this.c = c;
    }
}

- это ваш новомодный класс с внедренной зависимостью.

Пока все хорошо. Теперь давайте возьмем Collaborator и извлечем из него интерфейс; назовите это ICollaborator. Теперь ваш новый класс выглядит так:

class Service {
    ICollaborator c;

    Service(ICollaborator c) {
       this.c = c;
    }
}

Что это тебе покупает? Ну, вы можете в своем коде создать этот класс, который будет вести себя как первый пример:

// w00t!  My code compiles and works again!  Ship it!
Service myService = new Service(new Collaborator());

Довольно вырезать и легко высохнуть. Красота приходит от того, что вы хотите использовать другой тип Collaborator - возможно, даже издевательство или подделка. Пока он реализует интерфейс ICollaborator, вы великолепны:

// I'm using Fake It Easy for this example.
Service myService = new Service(A.Fake<ICollaborator>());

Вуаля! Теперь у вас есть проверяемый юнитом экземпляр Service, который не затягивает бетон Collaborator для поездки (что может нарушить истинное "юнит-тестирование").

1 голос
/ 06 октября 2011

Внедрение зависимостей - это просто практика не жесткого кодирования зависимостей в компонент.Например,

class Service {
   Collaborator c = new Collaborator()
}

для этого псевдокода жестко задан соавтор.Это трудно изменить.Если вы сделали

class Service {
    Collaborator c;

    Service(Collaborator c) {
       this.c = c;
    }
}

, то теперь вы можете «внедрить» нужного участника в компонент Service через конструктор.Нет жестко запрограммированной зависимости.

Это хорошо, так что вы можете легко менять реализации соавтора.Ваш код теперь «слабо связан» - нет жестких зависимостей от конкретных реализаций, только от типов и поведения.

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

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

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

0 голосов
/ 07 октября 2011

Чтобы добавить больше к обсуждению ...

Большую часть времени, когда люди говорят о DI, основным аргументом будет тестирование, но, как отметил Марк Симан (кстати, купите егокнига о DI, удивительная и очень поучительная, извините за рекламу), самый важный аспект - сделать ваше приложение слабо связанным и, следовательно, более легким в обслуживании.

Чтобы привести пример с тем же кодом, показанным вдругие ответы:

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

ICollaborator collaborator;

switch(timeOfYear)  
{  
    case "Spring":  
        collaborator = new SpringCollaborator();  
        break;  
    case "Summer":  
        collaborator = new SummerCollaborator();  
        break;  
    case "Fall":  
        collaborator = new FallCollaborator();  
        break;  
    case "Winter":  
        collaborator = new WinterCollaborator();  
        break;  
}  

Service myService = new Service(collaborator);

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

Существует гораздо больше информации о DI, но слабая связь и тестируемость всегдаДва преимущества, впервые указали.

С уважением.

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