Объясните этот мотивационный постер о принципе инверсии зависимости
В этом сообщении блога принцип инверсии зависимости был описан этим мотивационным постером:
Я не понимаю, что означает плакат: как пайка лампы непосредственно к стене нарушает принцип инверсии зависимости и как наличие вилок следует принципу инверсии зависимости. Может быть, какой-нибудь скелетный код Java или C# о лампах и электрических розетках может быть полезным.
2 ответа
По вашей ссылке:
"D" в SOLID означает принцип инверсии зависимости, который гласит, что высокоуровневые модули не должны зависеть от низкоуровневых модулей, но оба должны зависеть от общих абстракций.
Я не знаю, что они подразумевают под "общими" абстракциями. В коде лампа будет "модулем высокого уровня", и откуда она будет получать энергию, это ее зависимостью или "модулем низкого уровня".
Ваши вопросы:
Как пайка лампы прямо к стене нарушает принцип инверсии зависимости
Лампа на плакате нарушает принцип инверсии зависимости, потому что она не опирается на абстрактный источник энергии. Он опирается на очень конкретную реализацию припаивания непосредственно к электропроводке в стене. Если бы эти отношения были смоделированы в коде, это могло бы выглядеть примерно так:
public class Lamp {
// This part is the equivalent of "soldering a lamp directly to the electrical wiring in a wall"
private ElectricalWiringInBobsWall electricalWiring = new ElectricalWiringInBobsWall();
private boolean isOn;
public void turnOn() {
electricalWiring.useElectricityInWatts(60);
isOn = true;
}
public void turnOff() {
electricalWiring.turnOffElectricityInWatts(60);
isOn = false;
}
}
Следствием этого является то, что Лампа очень трудно использовать в других ситуациях. В реальной жизни сама лампа потребовала бы значительных переделок, если бы вы захотели перенести ее в другую часть дома, использовать ее на генераторе или даже взять за границу и подключить к другой розетке. Точно так же в кодовой базе, если вы хотите повторно использовать эту лампу с другим источником питания, вам придется изменить код в самой лампе, что может потребовать повторного тестирования, или ввести новые ошибки.
как вилки следуют принципу инверсии зависимости
Розетка более абстрактна, потому что, как пользователь, вам не обязательно знать, КАК лампа получает свою мощность. Все, что вам нужно знать, это то, что если вы подключите лампу, она будет работать. Розетка - это абстракция над источником питания. То есть он скрывает детали того, откуда на самом деле исходит сила. Это может быть розетка в стене, на генераторе, заграничном адаптере, на источнике питания солнечной панели и т. Д.
Используя DIP в нашем классе выше, мы хотим Lamp
зависеть от абстракции и передавать реализацию как зависимость. В коде это может выглядеть так:
public interface Outlet {
void useElectricityInWatts(int watts);
void turnOffElectricityInWatts(int watts);
}
public class Lamp {
private Outlet outlet;
private boolean isOn;
// We pass in the dependency instead of instantiating it and rely on an interface (abstraction)
public void plugInto(Outlet outlet) {
this.outlet = outlet;
}
public void turnOn() {
outlet.useElectricityInWatts(60);
isOn = true;
}
public void turnOff() {
outlet.turnOffElectricityInWatts(60);
isOn = false;
}
}
Теперь лампа может использовать любой тип розетки: BobsOutletInHisWall, GeneratorOutlet, EuropeanAdapterOutlet и т. Д. Ее можно легко перемещать и использовать в других ситуациях, и не нужно менять ее вообще.
Я знаю, что пример далек от совершенства, но я надеюсь, что это объясняет, что означает этот плакат.
Это аналогия обычной практики несложного кодирования, скажем, ваших контроллеров (классов, управляющих потоком), ваших моделей (откуда поступают ваши данные).
Например, это было бы плохо, не поддается проверке или легко "отключается":
public function index()
{
return $some->sql()->logic()->where('this', '=', 'that');
}
Вместо этого вы должны внедрить репозиторий в свой контроллер:
public function __construct(Somerepo $repo)
{
$this->repo = $repo;
}
Затем вы можете перейти к нужным операторам SQL с помощью методов в репозитории.
public function index()
{
return $this->repo->getStuff();
}
Таким образом, вы можете легко изменить или "отключить" источник данных, не касаясь логики контроллера.