SOLID – Zasada otwarte/zamknięte

Definicja

Zasada OCP (Open/Closed Principle) mówi, że klasy powinny być otwarte na rozszerzanie, ale zamknięte na modyfikacje. Oznacza to, że podczas projektowania klas należy zadbać o możliwość ich rozbudowy w przyszłości bez konieczności ingerowania w ich istniejący kod.

Wprowadzanie zmian do już istniejących klas wiąże się z ryzykiem – nie zawsze wiadomo, jak takie zmiany wpłyną na pozostałą część systemu. Dlatego zamiast modyfikować istniejące klasy, lepiej jest je rozszerzać.

W praktyce zasadę OCP można realizować za pomocą:

  • dziedziczenia (tworząc podklasy z nowym zachowaniem),
  • interfejsów (dzięki którym różne implementacje mogą być traktowane jednakowo),
  • wzorów projektowych

Dzięki temu kod staje się bardziej elastyczny, łatwiejszy w utrzymaniu i odporny na błędy wynikające z nieprzemywanych modyfikacji.

Przykład programu przed wdrożeniem OCP

public class Ceo {
    public void CalculateSalary() {
        System.out.println("Pay Ceo");
    }

    public void ShowIdCard() {
        System.out.println("Greet Ceo");
    }
}
public class Programmer {
    public void CalculateSalary() {
        System.out.println("Pay Programmer");
    }

    public void ShowIdCard() {
        System.out.println("Greet Programmer");
    }
}
public class Finances {
    public void CalculateSalaries(Object[] obiekty) {
        for (int i = 0; i < obiekty.length; i++) {
            if (obiekty[i] instanceof Ceo) {
                Ceo ceo = (Ceo) obiekty[i];
                ceo.CalculateSalary();
            }
            if (obiekty[i] instanceof Programmer) {
                Programmer programmer = (Programmer) obiekty[i];
                programmer.CalculateSalary();
            }
        }
    }
}
public class Hr {
    public void ShowIdCards(Object[] obiekty) {
        for (int i = 0; i < obiekty.length; i++) {
            if (obiekty[i] instanceof Ceo) {
                Ceo ceo = (Ceo) obiekty[i];
                ceo.ShowIdCard();
            }
            if (obiekty[i] instanceof Programmer) {
                Programmer programmer = (Programmer) obiekty[i];
                programmer.ShowIdCard();
            }
        }
    }
}
public class Program {
    public static void main(String[] args) {
        Finances finances = new Finances();
        Hr hr = new Hr();

        Object[] obiekty = new Object[] {new Ceo(), new Programmer()};

        finances.CalculateSalaries(obiekty);
        hr.ShowIdCards(obiekty);

    }
}

Gdy do aplikacji będzie trzeba w przyszłości dodać np. Księgowego utworzone w ten sposób klasy CalculateSalaries, ShowIdCard będzie trzeba zmodyfikować. Lepszym rozwiązaniem zgodnym z zasadą OCP jest zastosowanie np. implementacji z interfejsu. Rozwiązanie to pozwala na dodawanie kolejnych stanowisk pracy, gdy będzie taka potrzeba.

Kod po zastosowaniu OCP

public interface IEmployee {
    void CalculateSalary();
    void ShowIdCard();
}
public class Ceo implements IEmployee {
    @Override
    public void CalculateSalary() {
        System.out.println("Pay Ceo");
    }
    @Override
    public void ShowIdCard() {
        System.out.println("Greet Ceo");
    }
}
public class Programmer implements IEmployee {
    @Override
    public void CalculateSalary() {
        System.out.println("Pay Programmer");
    }
    @Override
    public void ShowIdCard() {
        System.out.println("Greet Programmer");
    }
}
public class Accountant implements IEmployee {
    @Override
    public void CalculateSalary() {
        System.out.println("Pay Accountant");
    }
    @Override
    public void ShowIdCard() {
        System.out.println("Greet Accountant");
    }
}
public class Finances {
    public void CalculateSalaries(IEmployee[] employees) {
        for (IEmployee employee : employees) {
            employee.CalculateSalary();
        }
    }
}
public class Hr {
    public void ShowIdCards(IEmployee[] employees) {
      for(IEmployee employee : employees) {
          employee.ShowIdCard();
      }
    }
}
public class Program {
    public static void main(String[] args) {
        Finances finances = new Finances();
        Hr hr = new Hr();

        IEmployee[] employees = new IEmployee[] {new Ceo(), new Programmer(),                
        new Accountant()};

        finances.CalculateSalaries(employees);
        hr.ShowIdCards(employees);
    }
}

Podsumowanie

Zastosowanie zasady OCP sprawia, że kod jest bardziej odporny na błędy, łatwiej się rozwija i nie wymaga modyfikacji już przetestowanych klas. To podstawa skalowalnych i solidnych aplikacji.

Tagi: Brak tagów

Add a Comment

Your email address will not be published. Required fields are marked *