Dependency Inversion Principle

Dependency Inversion Principle – DIP

Zasada odwrócenia zależności (Dependency Inversion Principle, DIP) mówi, że moduły powinny zależeć od abstrakcji, a nie od konkretnych implementacji. Oznacza to, że w kodzie powinny być używane interfejsy lub klasy abstrakcyjne, zamiast bezpośrednio operować na konkretnych klasach. Dzięki temu zmniejsza się zależność pomiędzy modułami i ułatwia wprowadzenie zmian czy wymianę konkretnych implementacji bez konieczności modyfikacji całego kodu

Zalety stosowania zasady DIP:

  • Luźne powiązania: Moduły są mniej zależne od szczegółów implementacji, co ułatwia zmiany w kodzie.
  • Łatwość testowania: Testowanie jest łatwiejsze, ponieważ można łatwo zamienić zależności na ich odpowiedniki w testach.
  • Lepsza skalowalność: Kod staje się bardziej modularny i skalowalny, ponieważ moduły wyższego poziomu nie są zależne od implementacji modułów niższego poziomu.

Przykład z życia codziennego:

Wybór sprzętu komputerowego:

Zły przykład: Kupujesz komputer, który jest zaprojektowany z wbudowanymi komponentami, takimi jak karta graficzna i pamięć RAM, które są zintegrowane i nie można ich łatwo wymieniać. Jeśli chcesz zaktualizować kartę graficzną lub zwiększyć pamięć RAM, musisz wymienić cały komputer, ponieważ nie ma możliwości wymiany tych komponentów osobno.

Dobry przykład (stosujący DIP): Kupujesz komputer, który ma standardowe złącza i porty dla karty graficznej, pamięci RAM i innych komponentów. Możesz łatwo wymieniać lub aktualizować poszczególne komponenty, takie jak karta graficzna czy pamięć RAM, bez potrzeby wymiany całego komputera. Komputer jest kompatybilny z różnymi producentami i typami komponentów, co ułatwia aktualizację.

Przykład programu przed użyciem zasady DIP:

W tym przykładzie klasa AudioPlayer jest bezpośrednio zależna od konkretnej klasy MP3Player.

public class MP3Player {
    public void play(String track) {
        System.out.println("Odtwarzanie utworu MP3: " + track);
    }

    public void stop() {
        System.out.println("Zatrzymywanie odtwarzania MP3.");
    }
}

// Klasa używająca MP3Player bez abstrakcji
public class AudioPlayer {
    private MP3Player mp3Player;

    public AudioPlayer(MP3Player mp3Player) {
        this.mp3Player = mp3Player;
    }

    public void playTrack(String track) {
        mp3Player.play(track);
    }

    public void stopPlayback() {
        mp3Player.stop();
    }
}


public class Main {
    public static void main(String[] args) {
        MP3Player mp3Player = new MP3Player();
        AudioPlayer audioPlayer = new AudioPlayer(mp3Player);
        audioPlayer.playTrack("Ulubiony utwór");
        audioPlayer.stopPlayback();
    }
}

W tym przypadku klasa AudioPlayer jest bezpośrednio zależna od klasy MP3Player. Jeśli chcesz dodać obsługę innych formatów audio, takich jak WAV lub FLAC, musisz zmienić kod klasy AudioPlayer.

Poprawiony kod zgodny z zasadą DIP

W tym przykładzie klasa AudioPlayer zależy od interfejsu MediaPlayer, a nie od konkretnej implementacji.

// Interfejs odtwarzacza audio
public interface MediaPlayer {
    void play(String track);
    void stop();
}

// Konkretny typ odtwarzacza MP3
public class MP3Player implements MediaPlayer {
    @Override
    public void play(String track) {
        System.out.println("Odtwarzanie utworu MP3: " + track);
    }

    @Override
    public void stop() {
        System.out.println("Zatrzymywanie odtwarzania MP3.");
    }
}

// Konkretny typ odtwarzacza WAV
public class WAVPlayer implements MediaPlayer {
    @Override
    public void play(String track) {
        System.out.println("Odtwarzanie utworu WAV: " + track);
    }

    @Override
    public void stop() {
        System.out.println("Zatrzymywanie odtwarzania WAV.");
    }
}

// Klasa używająca MediaPlayer
public class AudioPlayer {
    private MediaPlayer mediaPlayer;

    public AudioPlayer(MediaPlayer mediaPlayer) {
        this.mediaPlayer = mediaPlayer;
    }

    public void playTrack(String track) {
        mediaPlayer.play(track);
    }

    public void stopPlayback() {
        mediaPlayer.stop();
    }
}

// Klasa główna
public class Main {
    public static void main(String[] args) {
        MediaPlayer mp3Player = new MP3Player();
        AudioPlayer audioPlayerMP3 = new AudioPlayer(mp3Player);
        audioPlayerMP3.playTrack("Ulubiony utwór");
        audioPlayerMP3.stopPlayback();

        MediaPlayer wavPlayer = new WAVPlayer();
        AudioPlayer audioPlayerWAV = new AudioPlayer(wavPlayer);
        audioPlayerWAV.playTrack("Inny utwór");
        audioPlayerWAV.stopPlayback();
    }
}

Stosowanie tej pozwala na tworzenie bardziej elastycznego i łatwego w utrzymaniu kodu poprzez oddzielenie zależności od konkretnej implementacji i uzależnienie od abstrakcji. Dzięki temu system staje się bardziej modułowy i otwarty na zmiany, co ułatwia jego rozbudowę i testowanie.

Scroll to Top