Druga zasada mnemoniku SOLID. Praktyka mówiąca, że klasy powinny być otwarte na rozszerzenia, ale zamknięte na modyfikacje. Opracował ją Bertrand Meyer w 1988 r. Według niej dana encja powinna mieć możliwość zmiany swojego zachowania bez ingerowania w kod źródłowy. W ten sposób wytwarzamy system, który potrafi dostosować się do zmiennych wymagań.

Open/closed principle

Klasa zamyka się na modyfikacje, ale zawsze powinna być otwarta na rozszerzenie.

Tą zasadę można zilustrować następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Manager {

  private Task task;

  public Manager(task) {
    this.task = task;
  }

  public void work() {
    switch(task) {
      case ORGANIZE_MEETINGS:
        organizeMeetings();
        break;
      case MOTIVATE_TEAM:
        motivateTeam();
        break;
      default:
        haveDayOff();
    }
  }
}

public enum Task {
  ORGANIZE_MEETINGS, MOTIVATE_TEAM
}

Przy tworzeniu obiektu Manager przekazujemy parametr odpowiadający pracy jaką ma dzisiaj do wykonania. W przypadku, gdyby doszedł nowy typ zadania niezbędna byłaby ingerencja w kod źródłowy. Z tego powodu należałoby w instrukcji switch obsłużyć nowy rodzaj pracy. Jest to uciążliwe dla programisty i zdecydowanie łamie zasadę Otwarte-Zamknięte. Rozwiązaniem tego zagadnienia jest przechowywanie wspólnego, niezmiennego zachowania w abstrakcji (klasie abstrakcyjnej albo interfejsie). W efekcie odmienne części kodu źródłowego należy zamieścić w klasach konkretnych rozszerzających daną abstrakcję.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Manager {
  private Task task;

  public Manager(task) {
    this.task = task;
  }

  public void work() {
    task.getTheJobDone();
  }
}

public interface Task {
  void getTheJobDone();
}

public class OrganizeMeetingsTask implements Task {

  @Override  
  public void getTheJobDone() {
    // do task - organize meetings
  }
}

public class MotivateTeamTask implements Task {

  @Override  
  public void getTheJobDone() {
    // do task - motivate team
  }
}

Taki sposób zapisu jest o wiele przyjemniejszy w utrzymaniu. Dzięki czemu dodając nowe typy zadań nie istnieje potrzeba zmiany implementacji klasy Manager. W rezultacie nasz kod źródłowy na pewno jest otwarty na rozszerzenia i zamknięty na modyfikacje 👍.

Podsumowanie

Osobiście uważam, że zasada Open/Closed Principle jest bardzo przydatna. Dzięki niej nasz kod jest mniej ze sobą powiązany i nie uzyskujemy zależności pomiędzy elementami. Trzymanie się tej reguły naprowadza nas na bardzo przydatny wzorzec architektoniczny jakim jest Strategia. Zachęcam do dyskusji na temat zasady Otwarte-Zamknięte poprzez napisanie komentarza pod postem lub wysłanie do mnie maila. Czy posiada ona same zalety, a może jednak ma jakieś wady, które nie przemawiają za nią? Może jesteś w stanie lepiej zilustrować tą regułę? Z chęcią się zapoznam z Twoim punktem widzenia!