Рассмотрим теперь вариант 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 комьюнити
Спасибо за статьи!
ОтветитьУдалитьДобавил в закладки. уверен еще вернусь сюда.
ОтветитьУдалитьАвтор чтоб тебя добром завалило.