Рассмотрим теперь вариант legаcy code, когда у нас в конструкторе фонарика захардкоджен тип батарейки. Очень неприятный момент потому как классы сцеплены между собой жестко. Усугубим его тем фактом, что менять фонарик нельзя (маленькие рефакторинги допустимы, но как и раньше конструктор фонарика должен в себе содержать вызов конструктора батарейки). Заданием нашим будет проверить этот самый фонарик в отрыве от батарейки которую он упорно инстанциирует.
Вот фонарик.
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight() {
this.swithOn = false;
this.battery = new ChinaBattery(); // обратите внимание на эту наглость!
}
@Override
public boolean isShines() {
return (battery != null) && swithOn;
}
@Override
public void swithOn() {
if (!swithOn && battery != null) {
swithOn = battery.getVoltage();
}
}
@Override
public void swithOff() {
swithOn = false;
}
}Как его протестировать со другой батарейкой не добавляя никаких конструкторов, не нарушая инкапсуляцию объекта, изменением модификатора доступа поля battery?
Надо локализировать вызов конструктора ChinaBattery в отдельном методе, который позже переопределить в наследнике. Выглядит это так.
public class SomeFlashlight implements Flashlight {
private Battery battery;
private boolean swithOn;
public SomeFlashlight() {
this.swithOn = false;
this.battery = getBattery(); // вот отсюда ...
}
// ... вот сюда инкапсулировали мы зависимость.
// protected для того, чтобы можно было субклассировать и переопределить этот метод
protected Battery getBattery() {
return new ChinaBattery();
}
...А переопределять будем уже в тесте вот так
public class TestBaterry {
...
// внимание! это поле теста
private Battery battery;
// субклассируем SomeFlashlight и переопределяем в нем getBattery
class MockedSomeFlashlight extends SomeFlashlight {
@Override
protected Battery getBattery() {
return battery;
}
}
@Test
public void testDischargeNewBattery() {
// устанавливаем себе батарейку такую как захотим
this.battery = new DisposableBattery();
// и инстанциируем наш субкласс
MockedSomeFlashlight flashlight = new MockedSomeFlashlight();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
flashlight.swithOff();
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
...Вот так я иногда поступаю, когда в коде нельзя ничего делать ручками, а значит зависимость разорвать не получается, а значит DI через конструктор или сеттер не поможет.
Исходники этого примера можно качнуть тут.

спасибо за статьи про dependency injection, интересно было читать :)
ОтветитьУдалитьочень понятные статьи! Спасибо огромное!
ОтветитьУдалитьСпасибо, помогло!)
ОтветитьУдалитьСпасибо!
ОтветитьУдалитьСпасибо очень все ясно и доступно!
ОтветитьУдалитьСпасибо, побольше бы таких материалов от вас =) прекрасная подача!
ОтветитьУдалитьСпасибо за фидбек.
УдалитьС недавнего времени все подобные материалы публикуются в http://juja.com.ua комьюнити
Спасибо за статьи!
ОтветитьУдалитьДобавил в закладки. уверен еще вернусь сюда.
ОтветитьУдалитьАвтор чтоб тебя добром завалило.