Dependency Inversion Principle

(Zasada odwracania zależności)

1. Moduły wysokiego poziomu nie powinny zależeć od modułów niskiego poziomu. Oba powinny zależeć od abstrakcji (np. interfejsów lub klas abstrakcyjnych). 

2. Abstrakcje nie powinny zależeć od szczegółów. Szczegóły (konkretne implementacje) powinny zależeć od abstrakcji.

Robert C. Martin

moduł wysokiego poziomu – to moduł decydujący o logice  działania aplikacji (co ma robić nasza aplikacja a nie jak ma to zrobić). Do swojego działania potrzebuje modułów niskiego poziomu.

moduły niskiego poziomu – zapewniają dodatkową funkcjonalność, konkretną implementację sposobu wykonania jakiegoś zadania (zajmują się tym jak coś zrobić).

</> Lepszy przykład niż wykład

✘ Błędny kod

Klasa Horse przedstawia czym żywi się koń.

public class Horse {
    public void eating() {
        System.out.println("Zjadam owies.");
    }
}

W klasie Farmer rolnik zajmuje się karmieniem konia. W konstruktorze przypisujemy obiekt konia.

public class Farmer {
    private Horse horse;

    public Farmer(Horse horse) {
        this.horse = horse;
    }

    public void feedAnimal() {
        this.horse.eating();
    }
}

Co w sytuacji, gdy rolnik będzie miał karmić kolejne zwierzęta np. świnie?

Tworzymy klasę Pig.

Naruszamy dwie zasady SOLID:

  • OCP –  musimy zmodyfikować klasę Farmer o karmienie świń
  • DIP – nasz moduł wysokiego poziomu Farmer zależy od modułów niskiego poziomu Horse i Pig. Jakakolwiek zmiana modułów niskiego poziomu Horse i Pig wymusi zmiany w module wysokiego poziomu Farmer.
✔ Prawidłowy kod

Moduły wyższego poziomu nie powinny być zależne od modułów niższego poziomu. Obydwa powinny opierać się na abstrakcjach (klasach abstrakcyjnych lub interfejsach).
Utworzymy klasę abstrakcyjną FarmAnimal, na której będzie się opierał moduł wysokiego poziomu Farmer oraz moduły niskiego poziomu Horse i Pig.

public abstract class FarmAnimal {
    abstract void eating();
}

Klasy Horse i Pig rozszerzają metodę klasy FarmAnimal.

public class Horse extends FarmAnimal {
    @Override
    void eating() {
        System.out.println("Zjadam owies.");
    }
}

public class Pig extends FarmAnimal {
    @Override
    void eating() {
        System.out.println("Zjadam paszę.");
    }
}

Teraz nasz moduł wysokiego poziomu Farmer nie zależy już od modułów niskiego poziomu Horse i Pig.

public class Farmer {
    private FarmAnimal farmAnimal;

    public Farmer(FarmAnimal farmAnimal) {
        this.farmAnimal = farmAnimal;
    }

    public void feedAnimal() {
        this.farmAnimal.eating();
    }
}

Jeśli rolnik zechce karmić kolejne zwierzęta, wystarczy utworzenie klas tych zwierząt (Cow, Sheep itd.) rozszerzających klasę abstrakcyjną FarmAnimal i przekazanie obiektu danej klasy w konstruktorze Farmer(FarmAnimal farmAnimal).

public class Main {
    public static void main(String[] args) {
        Horse horse = new Horse();
        Pig pig = new Pig();
	    Cow cow = new Cow();
        Farmer farmer = new Farmer(horse);
        //Farmer farmer1 = new Farmer(pig);
	    //Farmer farmer2 = new Farmer(cow);

        farmer.feedAnimal();
        //farmer1.feedAnimal();
	    //Farmer2.feedAnimal();
    }
}

Zostaw komentarz

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