Open/Closed Principle – OCP

Open/Closed Principle – OCP

Zasada otwarte/zamknięte lub OCP to jest druga z zasad SOLID, która mówi nam, że jednostki oprogramowania, takie jak moduły, klasy, funkcje i tak dalej, powinny być otwarte na rozszerzanie, ale zamknięte na modyfikacje.

Czyli powinna istnieć możliwość zmiany zachowania metody
bez konieczności edytowania jej kodu źródłowego.

Wyobraźmy sobie teraz hipotetycznie, że mamy w naszej aplikacji metodę, która coś robi. Następnie przychodzi do nas nasz klient dla którego robimy aplikację i pojawia się nowy wymóg, który zmienia sposób działania naszej aplikacji w niektórych przypadkach. I to jest bardzo częsta, życiowa sytuacja która zdarza się nieustannie.

I typowe nasze podejście w takiej sytuacji, które podejmie pewnie większość programistów, co jest całkowicie uzasadnione i zrozumiałe, polega na dodaniu odpowiedniej logiki do naszej metody tak, że czasami nasza metoda robi jedną rzecz, a czasami coś innego.

No i to podejście jest dobre w większości przypadków. Jednak w miarę wzrostu złożoności naszej aplikacji powinniśmy pomyśleć o tym, jak możemy zastosować zasadę OCP, dzięki czemu nie będziemy musieli ciągle wykonywać modyfikacji tej samej metody za każdym razem, gdy tylko pojawia się nowy wymóg od naszego klienta.

I co należy rozumieć przez stwierdzenie, że moduł musi być „otwarty na rozszerzanie”?
Mianowicie to, że musi istnieć stosunkowo prosty sposób rozbudowy zachowań takiego modułu. Oznacza to, że jest łatwo dodać nowe zachowanie.

Z drugiej strony „Zamknięty dla modyfikację” co oznacza z kolei, że rozbudowa modułu nie może być przeprowadzona w sposób, który spowoduje zmianę istniejącego kodu źródłowego.

Nie powinniśmy dotykać istniejącego kodu, który pracuje w środowisku produkcyjnym co może być przyczyną błędów. Powinniśmy móc dokonać zmian poprzez rozszerzanie kodu a nie jego modyfikację. Powinniśmy tworzyć kod, który nie będzie musiał się zmieniać za każdym razem gdy zmienią się nasze wymagania biznesowe i przyjdzie klient z nowymi potrzebami.

Jakie mamy korzyści z OCP, dlaczego chcemy, aby nasz kod był zamknięty na modyfikacje a otwarty na rozszerzanie?

Otóż im mniej musimy zmieniać źródła naszego kodu, tym mniejsze jest prawdopodobieństwo, że wprowadzimy do niego nowe błędy.

Kod, który jest otwarty na rozszerzenie, zwykle zawiera mniej instrukcji warunkowych niż kod zamknięty do rozszerzenia, co oznacza, że jest to kod prostszy, który jest łatwiejszy do zrozumienia i testowania.

W naszej aktualnej implementacji klasy CarWash, istnieje instrukcja switch, która określa, jaki typ mycia został wybrany przez klienta. I Możemy sobie wyobrazić, że gdy aplikacja została pisana na początku, to obsługiwała tylko jeden typ mycia Standard, ale gdy pojawiły się dodatkowe wymagania i potrzeby firmy, dodane zostały kolejne typy mycia.

I te dodatkowe opcje które się pojawiły, zostały dodane do instrukcji switch i możemy być pewni że w przyszłości będą się pojawiać dodatkowe typy mycia, które będą musiały być obsługiwane przez naszą aplikację i zostaną dodane do instrukcji switch. Biorąc to pod uwagę powinniśmy zdecydowanie rozważyć refaktoryzację naszego kodu w celu zastosowania OCP.

I teraz tak istnieją cztery typowe podejścia do stosowania zasady otwarte/zamknięte Open/Closed Principle – OCP.

Po pierwsze, możemy użyć parametrów. Przekazując różne argumenty do metody, możemy zmienić jej zachowanie i to jest najprostsze podejście które jest doskonale nam znane.

Po drugie, możemy użyć dziedziczenia. Za pomocą dziedziczenie, możemy zmienić zachowanie typu bazowego po prostu poprzez utworzenie nowej klasy potomnej, która dziedziczy po nim.

Trzecie podejście to kompozycja i dependency injection (wstrzykiwanie zależności). Zamiast umieszczać logikę bezpośrednio w klasie, logika jest dostarczana przez inną klasę i zamiast zakodować na stałe odwołanie do tego innego typu, podane jest odwołanie za pomocą techniki znanej jako dependency injection. Porozmawiamy więcej o tej technice, gdy będziemy omawiać 5 zasadę Dependency Inversion czyli Zasada odwracania zależności.

Czwarte podejście za pomocą metod rozszerzających, które mogą dodawać dodatkowe funkcje do typów bez modyfikowania samych typów.

Po zidentyfikowaniu obszaru, w którym chcemy zastosować OCP w naszej aplikacji, powinniśmy preferować wdrażanie nowych funkcji w nowych klasach. Może to być szczególnie przydatne podczas pracy z dużymi, starszymi aplikacjami. Może to być sposobem na dodanie funkcjonalności bez zwiększania złożoności istniejącej aplikacji.

Dlaczego powinniśmy używać nowe klasy dla nowych funkcji, zwłaszcza w dużych starszych aplikacjach?

Po pierwsze możemy zaprojektować nową klasę od zera, aby jej konstrukcja idealnie pasowała do problemu, który próbujemy rozwiązać. Nie ma potrzeby obejścia istniejącego kod lub złego wzorca, który może tam być.

Po drugie, w obecnym systemie nic od tej klasy jeszcze nie zależy, ponieważ do tej pory nie istniała w naszym kodzie. Więc możemy go zbudować według własnego uznania.

Po trzecie nowe klasy pozwalają również na dodanie zachowania bez dotykania istniejącego kodu. W wielu starszych aplikacjach dotykanie istniejącego kodu może być niezwykle stresujące, a to pozwala tego uniknąć.

I Oczywiście każda nowa klasa, którą utworzymy, może natychmiast przestrzegać zasad SOLID. I wreszcie, ponieważ projektujemy ją od zera, możemy ją napisać tak, aby był łatwa do testowania.

Dobrze wystarczy już tych rozważań teoretycznych i przejdźmy do praktycznego zastosowania a wszystko stanie się jasne i proste, a najlepszy jest praktyczny, życiowy przykład, ale to już w następnym poście.

5 comments

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *