SOLID – #5 Zasada odwróconej zależności

SOLID jest to zestaw zasad dobrego programowania obiektowego. Stanowią fundament projektowania oprogramowania które mogą pomóc programistom tworzyć bardziej zorganizowane, elastyczne i łatwe w utrzymaniu systemy.

Piąta zasada SOLID jest to zasada odwróconej zależności(ang. dependency inversion principle) sformułowana przez Roberta C. Martin.

Jaka jest Definicja ?

Zgodnie z zasadą odwróconej zależności, moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu, ale oba rodzaje modułów powinny zależeć od abstrakcji. Zatem, wszystkie zależności powinny zależeć od abstrakcji, a nie od konkretnego typu.

Jak to osiągnąć?

Używając klas abstrakcyjnych lub interfejsów unikniemy bezpośrednich zależności od konkretnych implementacji. Do ustawiania zmiennych powinniśmy korzystać z wstrzykiwania zależności aby przekazywać odpowiednie zależności zamiast tworzyć je wewnątrz modułów, klas.

W przykładzie zaprezentowanym poniżej klasa Kawiarnia zależy bezpośrednio od konkretnej implementacji Latte, co jest niezgodne z zasadą odwróconej zależności. Ponadto, w przypadku chęci zmiany bądź rozszerzenia zamówienia o dodatkową kawę konieczna byłaby modyfikacja kodu w klasie Kawiarnia.

public class Kawiarnia {
    public Latte przygotujLatte;
    public Herbatnik przygotujHerbatnik;

    public Kawiarnia(){
        przygotujLatte = new Latte();
    }

    public void przygotujZamówienie(){
        przygotujLatte.przygotuj();
    }
    

public class App 
{
    public static void main( String[] args )
    {
        Kawiarnia kawiarnia = new Kawiarnia();
        kawiarnia.przygotujZamówienie();
    }
}

Zastosowanie odwróconej zależności umożliwia nam uniknięcie tego problemu. Wprowadzenie interfejsu IKawa pozwala bazować na typie abstrakcyjnym, a klasa Kawiarnia zależy już wyłącznie od interfejsu IKawa. Zastosowany wzorzec wstrzykiwania zależności przyczynił się do tego, że klasa Kawiarnia nie wie nic o klasie Latte. Mimo to pośrednio pracuje na niej poprzez interfejs IKawa.

public interface IKawa {
    public void przygotuj();
}

public class Latte implements IKawa {
    public void przygotuj(){
        System.out.println("Latte");
    }
}

public class Kawiarnia {
    private IKawa kawa;

    public Kawiarnia(IKawa kawa){
        this.kawa = kawa;
    }

    public void przygotujZamówienie(){
        this.kawa.przygotuj();
    }
}

public class App 
{
    public static void main( String[] args )
    {
        IKawa kawa = new Latte();
        Kawiarnia kawiarnia = new Kawiarnia(kawa);
        kawiarnia.przygotujZamówienie();
    }
}