Open Close Principle

(Zasada otwarte zamknięte)

Jednostki oprogramowania (klasy, moduły, funkcje itp.) powinny być otwarte na rozszerzanie, ale zamknięte na modyfikacje.

Meyer Bertrand

rozszerzenia vs modyfikacje

Jeśli użytkownik poprosi o wprowadzenie zmian w aplikacji, programista nie powinien modyfikować istniejących już klas, tylko powinien rozszerzyć istniejącą aplikację o nową klasę.
Więc jeśli chcemy zmienić aplikację, powinniśmy mieć możliwość zrobienia tego tylko przez dodanie nowych klas i funkcjonalności. Nie powinniśmy modyfikować istniejącego już kodu.

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

✘ Błędny kod

Klasa Cat zajmuje się wydawaniem dźwięku kota natomiast klasa Dog wydawaniem dźwięku psa

public class Cat {
    public void makeSound() {
        System.out.println("Wydaję dźwięk kota.");
    }
}

public class Dog {
    public void makeSound() {
        System.out.println("Wydaję dźwięk psa.");
    }
}

Klasa Sound decyduje dźwięk którego zwierzęcia ma zostać wydany.
Taki kod narusza zasadę OCP, ponieważ gdybyśmy chcieli poszerzyć działanie programu wydawanie dźwięków innych zwierząt (koń, owca itd.), musielibyśmy zmodyfikować klasę Sound dopisując kolejne warunki dla kolejnych zwierząt.

public class Sound {
    public static void makeSound(Object animal) {
        if (animal instanceof Cat) {
            ((Cat) animal).makeSound();
        } else if (animal instanceof Dog) {
            ((Dog) animal).makeSound();
        } //  else if (animal instanceof Horse) {...}; e.t.c  
    }
}

public class Main {
    public static void main(String[] args) {
        Sound sound = new Sound();
        Object animal = new Cat();
        Object animal1 = new Dog();

        sound.makeSound(animal);
        //sound.makeSound(animal1);
}
✔ Prawidłowy kod

Naruszenia zasad OCP możemy uniknąć, wykorzystując polimorfizm.

polimorfizm

(wielopostaciowość)

Pod obiekt danej klasy możemy przypisywać obiekty klas dziedziczących i podczas wywoływania tych samych metod ich zachowanie będzie inne (dostosowane do klasy dziedziczącej).

Czyli możemy utworzyć:

  • klasę abstrakcyjną (klasa abstrakcyjna nie może być inicjalizowana, nie możemy tworzyć obiektu tej klasy. Inne klasy mogą  dziedziczyć po niej i wykonywać gotowe metody oraz muszą nadpisywać jej metody abstrakcyjne)
  • lub interfejs (jest kompletną klasą abstrakcyjną – wszystkie metody w nim są abstrakcyjne i publiczne)

Następnie do obiektu tej klasy abstrakcyjnej lub interfejsu przypiszemy obiekt klasy dziedziczącej, a metoda wywołana na takim obiekcie wykona się zgodnie z implementacją klasy dziedziczącej.

Tworzymy interfejs Animal z metodą wydającą dźwięk zwierzęcia.

public interface Animal {
    void makeSound();
}

W klasach Cat i Dog wydających dźwięki konkretnych zwierząt implementujemy metodę interfejsu

public class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Wydaję dźwięk kota korzystając z polimorfizmu.");
    }
}

public class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Wydaję dźwięk psa korzystając z polimorfizmu.");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Cat();
        Animal animal1 = new Dog();
        Sound sound = new Sound();

        sound.makeSound(animal);
        //sound.makeSound(animal1);

    }
}

Następnie w naszej klasie Sound zostanie wydany dźwięk zwierzęcia przekazanego do metody makeSound(Animal animal).

public class Sound {
    public void  makeSound(Animal animal) {
        animal.makeSound();
    }
}

Ewentualne tworzenie nowych metod wydających dźwięki kolejnych zwierząt, zrealizujemy poprzez rozszerzenie naszego programu o kolejne klasy (Horse, Sheep itd.) implementujące interfejs Animal bez modyfikowania klasy Sound.

Zostaw komentarz

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