SOLID – część 5. Zasada odwrócenia zależności

SOLID – mnemonik, który mówi jak pisać poprawnie programy w językach obiektowych. Podążanie za tymi zasadami znacznie poprawia czytelność i możliwość utrzymywania kodu. Część piąta: Zasada odwrócenia zależności.

Dochodzimy do ostatniej zasady. Zasada odwrócenia zależności czyli Dependency inversion principle.
Spójrzmy co mówi DIP:

Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych – zależności między nimi powinny wynikać z abstrakcji.

Aby lepiej to zrozumieć o co chodzi spójrzmy na nasz aktualny kod:

class Program
{
	static void Main(string[] args)
	{
		var czolg = new Czolg()
	}
}

public class Czolg
{
	...
	public Czolg()
	{
		_pancerz = new Pancerz();
		_karabin = new CelnyKarain();
		_naped = new Kolka();
		_lufa = DlugaLufa();
	}

	...
}

Aktualnie tworząc klasę czołgu nie mamy wpływu no to, jaki ten czołg powstanie – z jakich elementów się będzie składać. To z jakich elementów ten czołg się będzie składać zapisane jest w konstruktorze czołgu. Czołg jest tutaj modułem wysokopoziomowym. Lufa, karabin, pancerz czy napęd są modułami niskopoziomowymi.

Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych

W tym momencie nasz czołg zależy od modułów niskopoziomowych. Jak temu zaradzić?

zależności między nimi powinny wynikać z abstrakcji

Wpierw odpowiedzmy sobie na kilka pytań.
Czym jest abstrakcja w naszym programie? Abstrakcją są interfejsy lub klasy bazowe (np. lufa).
Jak wprowadzić zależności pomiędzy modułami wysokopoziomowymi a niskopoziomowymi? Używając odwróconego sterowania (Inversion of Control – IoC).
Czym jest IoC? Jest to wzorzec, który po zaimplementowaniu spowoduje, że nasz kod będzie działać zgodnie z zasadą odwróconego sterowania.
Jak działa IoC? W naszym przykładzie będzie używać typu IoC Creation inversion. Zasada działa jest prosta: moduły wysokopoziomowe „same sobie wybierają” jakich modułów niskopoziomowych chcą używać. Aktualnie czołg ma na stałe wpisane jaki ma mieć napęd czy lufę.

Wprowadźmy do naszego programu IoC. A dokładniej użyjemy najpopularniejszej implementacji wzorca IoC czyli wstrzykiwanie zależności (Dependency Injection).

class Program
{
	static void Main(string[] args)
	{
		var pancerz = new Pancerz();
		var karabin = new CelnyKarabin();
		var naped = new Kolka();
		var lufa = new DlugaLufa();
		
		var czolg = new Czolg(pancerz, karabin, naped, lufa);
	}
}

public class Czolg
{
	...
	public Czolg(Pancerz pancerz, IStrzelajcy karabin, IJezdzacy naped, Lufa lufa)
	{
		_pancerz = pancerz;
		_karabin = karabin;
		_naped = naped;
		_lufa = lufa;
	}

	...
}

W tym momencie czołg sam sobie może wybrać jakich chce używać elementów. To my możemy kontrolować czołgiem z wysokiego poziomu, bez potrzeby wgłębiania się w implementację niskich poziomów.
W ten sposób nasz program spełnia teraz zasadę DIP. Używamy w tym celu DI, a dokładniej Constructor injection.

DIP a DI to nie to samo. DIP to Dependency inversion principle (zasada odwróconego sterowania), natomiast DI to Dependency Injection (wstrzykiwanie zależności). DI jest sposobem spełniania zasad odwróconego sterowania.

W naszym programie używamy jeszcze jednego wstrzykiwania zależności.

class Program
{
	static void Main(string[] args)
	{
		...
		var czolg = new Czolg(pancerz, karabin, naped, lufa);
		ISiejacyZniszczenie pocisk = new Pocisk();
		czolg.LadujPociks(pocisk);
	}
}

LadujPocisk jest przykładem DI Method injection. Z wysokiego poziomu mamy możliwość ustawić jaki pocisk chcemy załadować.

Jakie korzyści otrzymujemy dzięki używaniu DIP?

  • System teraz jest bardziej elastyczny.
  • Mamy większą pewność, że zmiana komponentu nie przyniesie niepożądanych skutków (wszystko działa na abstrakcjach).
  • Kod jest bardziej przenaszalny na inne projekty.

SOLID – Zasada odwrócenia zależności. Linki.

Podstawy DIP
Wytłumaczenie IoC
Świetnie opisany DIP, IoC oraz DI
Seria artykułów o IoC oraz DI

Leave a Comment

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *