OCP (Open/Closed Principle) — druhý princip SOLID, který stanovuje: softwarové entity by měly být otevřené pro rozšíření, ale uzavřené pro modifikaci. Tento princip, formulovaný Bertrandem Meyerem v roce 1988, umožňuje přidávat novou funkcionalitu bez změny existujícího kódu. Podle knihy Roberta C. Martina Clean Architecture (2017), je princip otevřenosti implementován prostřednictvím abstrakcí a polymorfismu, čímž se minimalizuje riziko regresních chyb.
Hlavní body
OCP (Open/Closed Principle) — princip otevřenosti pro rozšíření a uzavřenosti pro modifikaci. Třídy, moduly a funkce by měly být navrženy tak, aby nové chování bylo přidáno bez změny jejich zdrojového kódu. Rozšíření je dosaženo prostřednictvím dědičnosti, kompozice nebo substituce implementací rozhraní.
Bertrand Meyer v knize Object-Oriented Software Construction (1988) poprvé popsal OCP prostřednictvím dědičnosti: základní třída zůstává nezměněna a podtřídy rozšiřují její chování. Moderní výklad OCP, navržený Robertem C. Martinem, se opírá o polymorfismus a rozhraní: místo dědičnosti se používají abstraktní kontrakty.
Rozdíl mezi přístupy je významný. Dědičnost vytváří pevné propojení mezi základní a odvozenou třídou. Rozhraní a kompozice poskytují flexibilitu: implementace je nahrazena bez změny klientského kódu. Moderní OCP — je o abstrakci, ne o dědičnosti.
Polymorfní OCP používá abstraktní třídy nebo rozhraní k definování kontraktu. Klientský kód pracuje s abstrakcí, aniž by znal konkrétní implementaci. Nová funkcionalita se přidává vytvořením nové třídy implementující stejné rozhraní — bez jakékoli změny existujícího kódu. To činí systém odolným vůči změnám a předvídatelným pro rozšíření.
V mobilním vývoji je tento přístup všudypřítomný: vzor Strategy umožňuje nahrazovat algoritmy (komprese obrázků, ukládání do mezipaměti, autentizace) prostřednictvím společného rozhraní. Přidání nové strategie nevyžaduje změnu kódu, který ji používá.
Implementace OCP začíná oddělením proměnlivého chování do abstrakce. Pokud je v kódu konstrukce switch nebo řetězec if-else kontrolující typ objektu — to je signál k aplikaci OCP. Každá větev podmínky potenciálně vyžaduje přidání nové větve při rozšíření.
Proces refaktorování podle OCP zahrnuje tři kroky: identifikovat proměnlivý aspekt (co může být rozšířeno), oddělit jej do rozhraní nebo abstraktní třídy, přepsat klientský kód pro práci s abstrakcí namísto konkrétní třídy. Poté je nová funkcionalita přidána bez změny klienta.
Důležité upřesnění: uzavřenost pro modifikaci není absolutní. Pokud se požadavek na změnu týká samotné abstrakce nebo kontraktu — změna je nevyhnutelná. OCP chrání před změnami v implementacích, ne v kontraktech. Dobrý design předpokládá, že kontrakty jsou stabilní a implementace proměnlivé.
Při hodnocení kompatibility architektury s OCP je užitečné dívat se na body rozšíření. Každý bod, kde vývojář přidává if-else nebo switch pro nový typ — kandidát na abstrakci. Systém navržený podle OCP má předvídatelné body rozšíření: rozhraní s dokumentací „implementuj toto rozhraní pro přidání nového typu“. V Androidu je takovým příkladem vzor Factory ve spojení s ViewModelProvider.Factory — přidání nového typu ViewModel nevyžaduje změnu existujících továren.
Nejefektivnější vzory pro dodržování OCP v mobilním vývoji zahrnují Strategy, Template Method, Decorator a Factory. Každý z nich řeší problém rozšíření chování bez modifikace existujícího kódu prostřednictvím různých mechanismů objektově orientovaného návrhu.
Strategy umožňuje nahrazovat algoritmy za běhu prostřednictvím společného rozhraní. Ve vývoji pro iOS se strategie používají pro animace a validaci formulářů. Template Method definuje kostru algoritmu v základní třídě a podtřídy přepisují kroky — vhodné pro obrazovky se společnou strukturou, ale odlišným obsahem.
Decorator dynamicky přidává chování objektu bez změny jeho třídy. V Androidu se Decorator aplikuje na obalení Repository vrstvou cache nebo logování. Factory Method vytváří objekty prostřednictvím rozhraní a umožňuje podtřídám rozhodnout, kterou třídu instanciovat — základ vytváření závislostí kompatibilního s OCP.
Výběr vzoru závisí na stabilitě rozšiřovaného chování. Strategy je optimální, když jsou algoritmy zcela nahrazovány. Template Method — když je struktura pevná, ale kroky proměnlivé. Decorator — když má být rozšíření transparentní pro klienta. Pro většinu scénářů v Androidu a iOS stačí Strategy + vkládání závislostí.
Aplikace těchto vzorů bez OCP je technicky možná, ale ztrácí smysl. Právě OCP ospravedlňuje, proč zavádíme další úroveň abstrakce: aby systém mohl růst bez přepisování existujícího kódu.
Podívejme se na příklad z Androidu se zpracováním plateb. Bez OCP každý nový platební systém vyžaduje změnu zpracovatelské třídy. S OCP se přidá nová implementace rozhraní bez úpravy existujícího kódu.
// Porušení OCP: switch vyžaduje změnu pro nový systém
class BadPaymentProcessor {
fun process(type: String) {
when (type) {
"card" -> // zpracování karty
"paypal" -> // zpracování PayPal
}
}
}
// Design kompatibilní s OCP
interface PaymentMethod {
fun pay(amount: Double)
}
class CardPayment : PaymentMethod {
override fun pay(amount: Double) { }
}
class PayPalPayment : PaymentMethod {
override fun pay(amount: Double) { }
}
// Nový systém — nová třída, bez změny existujícího kódu
class ApplePayPayment : PaymentMethod {
override fun pay(amount: Double) { }
}
Příklad z iOS s validací textových polí demonstruje stejnou logiku prostřednictvím protokolů Swift:
// Validace kompatibilní s OCP
protocol ValidationRule {
func validate(_ input: String) -> Bool
}
struct EmailRule: ValidationRule {
func validate(_ input: String) -> Bool {
return input.contains("@")
}
}
struct PhoneRule: ValidationRule {
func validate(_ input: String) -> Bool {
return input.count == 11
}
}
// Přidání nového pravidla nevyžaduje změnu kódu validátoru
struct PasswordRule: ValidationRule {
func validate(_ input: String) -> Bool {
return input.count >= 8
}
}
Klíčová výhoda OCP v těchto příkladech: přidání ApplePay nebo PasswordRule nevyžaduje změnu existujících tříd. Kód se rozšiřuje horizontálně — prostřednictvím nových souborů, nikoli změnou starých. To snižuje riziko regrese a urychluje zavádění nové funkcionality.
Nejčastější porušení — konstrukce switch nebo when podle typu objektu. Při každém přidání nového typu je třeba najít všechny takové switch v kódu a přidat novou větev. Vynechaný switch — chyba za běhu, kterou je obtížné odhalit ve fázi kompilace.
V mobilním vývoji je OCP porušováno při použití obřích enum tříd s metodami závislými na hodnotě enum. Přidání nového prvku enum vyžaduje změnu každého switch v celém projektu. Alternativou je polymorfismus prostřednictvím rozhraní, kde každý typ implementuje své vlastní chování.
Dalším typickým porušením je God Adapter: RecyclerView.Adapter (Android) nebo UITableViewDataSource (iOS), který prostřednictvím if-else zpracovává různé typy buněk. Každý nový typ buňky vyžaduje rozšíření adaptéru. Řešením je polymorfní ViewHolder se společnou metodou bind, kde každý typ buňky odpovídá za své vlastní zobrazení.
Preventivní opatření zahrnují: opuštění switch podle typu ve prospěch polymorfismu, vkládání závislostí prostřednictvím rozhraní a aplikaci vzoru Factory pro vytváření objektů podle konfigurace. Analýza kódu na „přepínače podle typu“ — povinná součást code review v OCP-orientovaných týmech.
Refaktorování existujícího porušení OCP se provádí pomocí Replace Conditional with Polymorphism: každá větev podmínky se stává samostatnou třídou s implementací společného rozhraní. Klientský kód je přepsán pro práci s rozhraním a konkrétní implementace je dodávána prostřednictvím továrny nebo DI kontejneru.
Je důležité pochopit, že OCP a polymorfismus neřeší všechny problémy rozšiřování. Pokud je architektura zvolena nesprávně, přidání nové funkcionality bude vyžadovat změnu nejen implementací, ale i kontraktů. Dobrá architektura předvídá směry rozšíření a umisťuje abstrakce právě v těchto bodech. Investice do OCP se vyplácí tím více, čím déle projekt žije a čím častěji se mění požadavky na konkrétní moduly.
Často kladené otázky
Ne. OCP zakazuje změnu existujícího kódu při přidávání nové funkcionality patřící ke stejné abstrakci. Změna kontraktu, oprava chyb a refaktorování nejsou porušením OCP — princip chrání před řetězovými změnami při rozšiřování.
Strategy — přímá implementace OCP. Rozhraní strategie definuje kontrakt, klient závisí na abstrakci a konkrétní strategie implementují proměnlivé chování. Přidání nové strategie nevyžaduje změnu klienta — to je otevřenost pro rozšíření při uzavřenosti pro modifikaci.
Ano, prostřednictvím dědičnosti a Template Method: základní třída definuje kostru algoritmu, podtřídy přepisují kroky. Dědičnost však vytváří pevné propojení a je méně flexibilní než rozhraní. V moderním vývoji jsou rozhraní a kompozice považovány za preferovaný způsob implementace OCP.
Kód kompatibilní s OCP zjednodušuje testování: každá implementace rozhraní je testována izolovaně. Klientský kód je testován s mock implementací, což umožňuje ověřit logiku bez vazby na konkrétní chování. Rozšíření systému nevyžaduje přepisování existujících testů.
Ne. OCP je ospravedlnitelný, když je rozšíření funkcionality předvídatelné. Pro stabilní kód, který není plánován k rozšiřování, je další abstrakce zbytečná. YAGNI (You Ain't Gonna Need It) — dobrá protiváha OCP: abstrakce se zavádí, když se objeví druhá varianta chování, nikoli předem.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také