Definicja
Zasada otwarte/zamknięte (ang. Open/Closed Principle, OCP) mówi, że klasy, moduły i funkcje powinny być otwarte na rozszerzenia, ale zamknięte na modyfikacje.
Oznacza to, że jeśli chcemy dodać nową funkcjonalność, powinniśmy móc to zrobić bez zmieniania istniejącego kodu, tylko poprzez jego rozszerzenie – np. przez dziedziczenie lub implementację interfejsu.


Przykład z życia
Wyobraźmy sobie fabrykę prefabrykatów W fabryce prefabrykatów istnieje linia produkcyjna, która może wytwarzać różne elementy: belki, płyty i słupy.
Na początku proces sterowania maszyną był przygotowany tylko dla belek. Gdy pojawiła się potrzeba produkcji płyt, trzeba było zmieniać oryginalny kod programu sterującego, co groziło wprowadzeniem błędów.
Lepszym rozwiązaniem jest stworzenie systemu, który można rozszerzyć o nowe typy prefabrykatów, bez ingerowania w już działający kod. Każdy nowy typ elementu ma wtedy własny moduł produkcyjny, który wpasowuje się w istniejącą strukturę.
Przykłady przed i po zastosowaniu zasady
PRZED
Poniższy przykład łamie zasadę OCP — każdorazowe dodanie nowego typu prefabrykatu wymaga modyfikacji klasy ProductionController.
Aby dodać np. słup prefabrykowany, trzeba otworzyć klasę i dodać kolejny warunek elif element_type == "column": ....
Z czasem taka klasa staje się trudna w utrzymaniu i podatna na błędy.
public class ProductionController {
public void produce(String elementType) {
if (elementType.equals("beam")) {
System.out.println("Producing reinforced concrete beam...");
} else if (elementType.equals("slab")) {
System.out.println("Producing reinforced concrete slab...");
} else {
System.out.println("Unknown element type!");
}
}
public static void main(String[] args) {
ProductionController controller = new ProductionController();
controller.produce("beam");
controller.produce("slab");
controller.produce("column");
}
}

PO
Po zastosowaniu zasady OCP rozdzielamy odpowiedzialności i umożliwiamy rozszerzanie funkcjonalności bez modyfikacji istniejącego kodu.
Teraz jeśli chcemy dodać nowy element, np. słup, wystarczy utworzyć nową klasę. I można ją natychmiast użyć — bez modyfikowania ProductionController.
// Klasa bazowa (abstrakcyjna)
abstract class PrefabElement {
public abstract void produce();
}
// Klasa reprezentująca belkę prefabrykowaną
class Beam extends PrefabElement {
@Override
public void produce() {
System.out.println("Producing reinforced concrete beam...");
}
}
// Klasa reprezentująca płytę prefabrykowaną
class Slab extends PrefabElement {
@Override
public void produce() {
System.out.println("Producing reinforced concrete slab...");
}
}
// Kontroler produkcji – działa z dowolnym prefabrykatem
class ProductionController {
private PrefabElement element;
public ProductionController(PrefabElement element) {
this.element = element;
}
public void runProduction() {
element.produce();
}
}
// Klasa główna – przykład użycia
public class Main {
public static void main(String[] args) {
PrefabElement beam = new Beam();
PrefabElement slab = new Slab();
ProductionController controller1 = new ProductionController(beam);
controller1.runProduction();
ProductionController controller2 = new ProductionController(slab);
controller2.runProduction();
}
}
Kiedy zasada jest ważna?
Zasada otwarte/zamknięte jest kluczowa, gdy system ma rosnąć lub obsługiwać różne warianty elementów prefabrykowanych (np. różne typy zbrojeń, form, betonu).
Dzięki niej możemy łatwo dodawać nowe typy operacji lub produktów, nie ryzykując zepsucia działającego kodu.
Na co zwracać uwagę:
- Aby kod był łatwy do rozszerzenia przez dziedziczenie lub interfejsy,
- Aby unikać rozbudowanych instrukcji
ifiswitch, które łamią zasadę OCP.
Trudności:
- Początkujący często „nadmiernie abstrakcyjnie” projektują kod, co utrudnia jego zrozumienie,
- Należy znaleźć balans między elastycznością a prostotą – OCP nie oznacza, że każda funkcja wymaga osobnej klasy.
