Рассматривать будем 4 случая инъекции:
- инъекция через конструктор
- инъекция через setter
- подготовка объекта с помощью простой фабрики
- подготовка объекта с помощью IoC контейнера написанного собственноручно
Действующие лица:
- какой-то фонарик - class SomeFlashlight
- описание фонарика - integface Flashlight
- описание батарейки - interface Battery
- китайская батарейка - class ChinaBattery
Скажу сразу исходники этого примера можно качнуть тут.
Батарейка - с ней все просто, она дает некоторое напряжение (true) или не дает его (false), если севшая.
package constructor.battery; public interface Battery { boolean getVoltage(); }
По задумке батарейка разряжается от последовательных вызовов ее метода getVoltage, что реализовано в китайской батарейке
package constructor.battery; public class ChinaBattery implements Battery { private int power = 5; @Override public boolean getVoltage() { if (power > 0) { power--; return true; } return false; } }
Так как она китайская, то садится за 5 раз.
Идем дальше - интерфейс фонарик
package constructor.flashlight; public interface Flashlight { void swithOn(); void swithOff(); boolean isShines(); }
Фонарик может включаться (swithOn) и выключаться (swithOff) а светится он или нет, мы узнаем с помощью метода isShines, возвращающего true/false.
Если посмотреть на реализацию, то станет видно, что свет горит (isShines = true) только, если батарейка есть + если она дает заряд. При включении с батарейки снимается заряд, что равносильно ее разрядке, если вспомнить реализацию китайской одноразки.
package constructor.flashlight; import constructor.battery.Battery; public class SomeFlashlight implements Flashlight { private Battery battery; private boolean swithOn; public SomeFlashlight(Battery battery) { this.battery = battery; this.swithOn = false; } @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; } }
Вот тест, демонстрирующий работу фонарика
package constructor; import static org.junit.Assert.*; import constructor.battery.Battery; import constructor.battery.ChinaBattery; import constructor.flashlight.Flashlight; import constructor.flashlight.SomeFlashlight; import org.junit.Test; public class TestBaterry { class DisposableBattery implements Battery{ private boolean full = true; @Override public boolean getVoltage() { if (full) { full = false; return true; } return false; } } @Test public void testDischargeNewBattery() { Battery battery = new DisposableBattery(); Flashlight flashlight = new SomeFlashlight(battery); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertTrue(flashlight.isShines()); flashlight.swithOff(); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertFalse(flashlight.isShines()); } @Test public void testBadBattery() { Battery battery = new Battery(){ @Override public boolean getVoltage() { return false; } }; Flashlight flashlight = new SomeFlashlight(battery); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertFalse(flashlight.isShines()); } @Test public void testNoGetPowerIfDoubleSwithOn() { Battery battery = new DisposableBattery(); Flashlight flashlight = new SomeFlashlight(battery); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertTrue(flashlight.isShines()); flashlight.swithOn(); assertTrue(flashlight.isShines()); } @Test public void testNoBatteryNoLight() { Flashlight flashlight = new SomeFlashlight(null); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertFalse(flashlight.isShines()); } @Test public void integrationTestGetPowerFormNewChinaBattery() { Battery battery = new ChinaBattery(); Flashlight flashlight = new SomeFlashlight(battery); assertFalse(flashlight.isShines()); flashlight.swithOn(); assertTrue(flashlight.isShines()); } }
Если посмотреть на каждый тест, то тут что происходит
Battery battery = ... Flashlight flashlight = new SomeFlashlight(battery); ...
То есть вставляется через конструктор какая-то батарейка. Внутри фонарика все равно какая это будет батарейка, лишь бы соответствовала интерфейсу
public class SomeFlashlight implements Flashlight { private Battery battery; ... public SomeFlashlight(Battery battery) { this.battery = battery; ... } ...
Конструктор фонарика мило подставит любую батарейку (которая, implements Battery) в свое поле. Это позволит в дальнейшем пользоваться из фонарика всеми методами, объявленными в интерфейсе Battery
public interface Battery { boolean getVoltage(); }
Вот так
public class SomeFlashlight implements Flashlight { ... @Override public void swithOn() { if (!swithOn && battery != null) { swithOn = battery.getVoltage(); } } ...
Любая батарейка (то есть любой класс, главное, чтобы implements Battery) может быть использована фонариком. Фонарик при включении (swithOn()) попросит у батарейки энергии (battery.getVoltage()).
Вообще, так это полиморфизм на интерфейсах - результат выполнения battery.getVoltage() будет зависеть от того, что находится в battery.
И это первый вариант инъекции зависимости - через конструктор. Используется она тогда, когда фонарик не может существовать без батарейки. Представь себе такой одноразовый фонарик, в который вмонтирована батарейка и доступа к ней нет. Села батарейка - выкидываешь фонарик.
Это называется композицией, когда части объекта неотъемлемы от него. Вот хлеб с изюмом состоит из муки, воды, соли, изюма - все это разобрать по частям не возможно. Хлеб - это композит. Раз создали используем все вместе.
Если с хлебом это прокатило, то с фонариком это кажется немного нелогичным. Мы привыкли видеть фонарики в которых можно заменять батарейки, как только те сели. Тут нам поможет другой способ инъекции зависимости - с помощью setter.
Но об этом в следующей серии "инъекция через setter"...
Ты мега-понятно объясняешь! И пример суперский!
ОтветитьУдалитьСпасибо. Бывает иногда как нахлынет, могу часы напролет клепать примерчики для блога...
ОтветитьУдалитьТолько благодаря вашему блогу понял что такое Dependency injection, Inversion of Control. Как оказалось за этими неочевидными словами скрываются простые и понятные принципы.
ОтветитьУдалитьСпасибо.
Спасибо и вам за отзыв. Но есть одно но - не верьте мне на слово. Экспериментируйте! А потом напишите в своем блоге о вашем опыте, о том как вы понимаете эту тему. Именно тогда вы начнете чувствовать предмет, а не только знать.
ОтветитьУдалитьУспехов!
мега круто чувак)) сенкс
ОтветитьУдалитьЗачетный примерчик. Спасибо огромное. Успехов...
ОтветитьУдалитьБольшое спасибо! Классный пример. Всё доходчиво объяснено.
ОтветитьУдалитьБлагодарю!
ОтветитьУдалитьКласс, реально понятно!!!
ОтветитьУдалитьШиКарНо
ОтветитьУдалитьСпасибо! Очень понятно объяснили то, что слишком заумно везде подается, особенно, когда с наскоку показывают на Spring.
ОтветитьУдалитьИ Понравились примеры с батарейками ;)
Этот комментарий был удален автором.
ОтветитьУдалитьЭтот комментарий был удален автором.
ОтветитьУдалить