Niedawno czytam witrynę o czystym programowaniu (nie zamieszczam tutaj linku, ponieważ nie jest w języku angielskim).
Jedną z zasad reklamowanych na tej stronie jest zasada otwartej zamkniętej : każdy składnik oprogramowania powinien być otwarty na rozszerzenie i zamknięty na modyfikację. Na przykład, kiedy zaimplementowaliśmy i przetestowaliśmy klasę, powinniśmy ją modyfikować tylko w celu naprawienia błędów lub dodania nowej funkcjonalności (np. Nowych metod, które nie wpływają na istniejące). Istniejącej funkcjonalności i implementacji nie należy zmieniać.
Zazwyczaj stosuję tę zasadę, definiując interfejs I
i odpowiednią klasę implementacji A
. Kiedy klasa A
stała się stabilna (zaimplementowana i przetestowana), zwykle nie modyfikuję jej zbyt mocno (prawdopodobnie wcale), tj
- Jeśli pojawią się nowe wymagania (np. Wydajność lub zupełnie nowa implementacja interfejsu), które wymagają dużych zmian w kodzie, piszę nową implementację
B
i używamA
tak długo, jak długoB
nie jest dojrzała. KiedyB
jest dojrzały, wystarczy zmienić sposóbI
tworzenia instancji. - Jeśli nowe wymagania sugerują również zmianę interfejsu, definiuję nowy interfejs
I'
i nową implementacjęA'
. TakI
,A
są zamrożone i pozostaje wdrożenie do systemu produkcyjnego jak długoI'
iA'
nie są wystarczająco stabilne, aby je zastąpić.
Tak więc, w świetle tych spostrzeżeń, byłem nieco zaskoczony, że strona internetowa zasugerowała następnie użycie złożonych refaktoryzacji : „... ponieważ nie jest możliwe napisanie kodu bezpośrednio w jego ostatecznej formie”.
Czy nie ma sprzeczności / konfliktu między egzekwowaniem zasady otwartej / zamkniętej a sugerowaniem stosowania złożonych refaktoryzacji jako najlepszej praktyki? A może chodzi o to, że podczas opracowywania klasy można zastosować złożone refaktoryzacje A
, ale kiedy ta klasa zostanie pomyślnie przetestowana, należy ją zamrozić?
Zasada Otwarte-Zamknięte jest raczej wskaźnikiem tego, jak dobrze zostało zaprojektowane twoje oprogramowanie ; nie jest to zasada, której należy przestrzegać dosłownie. Jest to także zasada, która pomaga nam uniknąć przypadkowej zmiany istniejących interfejsów (wywoływane przez ciebie klasy i metody oraz oczekiwanie, że będą działać).
Celem jest pisanie wysokiej jakości oprogramowania. Jedną z tych cech jest rozszerzalność. Oznacza to, że łatwo jest dodawać, usuwać i zmieniać kod, przy czym zmiany są ograniczone do jak najmniejszej liczby istniejących klas. Dodanie nowego kodu jest mniej ryzykowne niż zmiana istniejącego kodu, więc pod tym względem dobrze jest zrobić Open-Closed. Ale o jakim kodzie dokładnie mówimy? Przestępstwo polegające na naruszeniu OC jest znacznie mniejsze, gdy możesz dodać nowe metody do klasy zamiast zmieniać istniejące.
OC jest fraktalna . Jabłka na wszystkich głębokościach Twojego projektu. Wszyscy zakładają, że jest stosowany tylko na poziomie klasy. Ale ma to również zastosowanie na poziomie metody lub na poziomie zespołu.
Zbyt częste naruszanie OC na odpowiednim poziomie sugeruje, że być może nadszedł czas na refaktoryzację . „Odpowiedni poziom” to wezwanie do osądu, które ma wszystko wspólnego z twoim ogólnym projektem.
Dosłownie następujące „Otwarte-Zamknięte” oznacza, że liczba klas wybuchnie. Niepotrzebnie utworzysz (duże „I”) interfejsy. Skończysz z częściami funkcjonalności rozłożonymi na klasy, a następnie będziesz musiał napisać dużo więcej kodu, aby połączyć to wszystko razem. W pewnym momencie zrozumie, że zmiana oryginalnej klasy byłaby lepsza.
źródło
Wydaje się, że zasada Open-Closed była bardziej rozpowszechniona. Chodzi o to, że refaktoryzacja kodu jest ryzykowna, ponieważ możesz coś zepsuć, więc bezpieczniej jest pozostawić istniejący kod w obecnej postaci i po prostu go dodać. W przypadku braku testów ma to sens. Minusem tego podejścia jest zanik kodu. Za każdym razem, gdy rozszerzasz klasę, zamiast ją refaktoryzować, dostajesz dodatkową warstwę. Po prostu nakładasz kod na wierzch. Za każdym razem, gdy naciskasz więcej kodu, zwiększasz szansę na powielenie. Wyobrażać sobie; w mojej bazie kodu jest usługa, z której chcę korzystać, okazuje się, że nie ma tego, czego chcę, dlatego tworzę nową klasę, aby ją rozszerzyć i uwzględnić moją nową funkcjonalność. Inny programista pojawia się później i również chce skorzystać z tej samej usługi. Niestety nie Zdaję sobie sprawę, że moja rozszerzona wersja istnieje. Kodują w stosunku do oryginalnej implementacji, ale potrzebują także jednej z kodowanych przeze mnie funkcji. Zamiast korzystać z mojej wersji, teraz rozszerzają implementację i dodają nową funkcję. Teraz mamy 3 klasy, oryginalną i dwie nowe wersje, które mają zdublowane funkcje. Postępuj zgodnie z zasadą otwartego / zamkniętego, a to powielanie będzie się powiększać przez cały czas trwania projektu, co prowadzi do niepotrzebnie złożonej bazy kodu.
Dzięki dobrze przetestowanemu systemowi nie ma potrzeby atrofii tego kodu, możesz bezpiecznie refaktoryzować kod, umożliwiając projektowi przyswojenie nowych wymagań zamiast konieczności ciągłego wprowadzania nowego kodu. Ten styl rozwoju nazywa się wschodzącym projektowaniem i prowadzi do baz kodu, które są w stanie zachować dobrą formę przez cały okres użytkowania, zamiast stopniowo gromadzić cruft.
źródło
Słowami laika:
A. Zasada O / C oznacza, że specjalizacji należy dokonać poprzez rozszerzenie, a nie modyfikację klasy w celu dostosowania do specjalnych potrzeb.
B. Dodanie brakującej (niespecjalistycznej) funkcjonalności oznacza, że projekt nie był kompletny i musisz dodać go do klasy podstawowej, oczywiście bez naruszenia umowy. Myślę, że to nie narusza zasady.
C. Refaktoryzacja nie narusza zasady.
Kiedy projekt dojrzewa , powiedzmy, po pewnym czasie w produkcji:
źródło
Według mnie Zasada Otwartego Zamknięcia jest wytyczną, a nie twardą i szybką zasadą.
Jeśli chodzi o otwartą część zasady, końcowe klasy w Javie i klasy w C ++ ze wszystkimi konstruktorami zadeklarowanymi jako prywatne naruszają otwartą część zasady otwartego zamknięcia. Istnieją dobre solidne przypadki użycia (uwaga: stałe, a nie SOLIDNE) dla klas końcowych. Ważne jest zaprojektowanie rozszerzalności. Jednak wymaga to dalekowzroczności i wysiłku, a ty zawsze omijasz linię naruszania YAGNI (nie będziesz go potrzebował) i wstrzykujesz kodowy zapach spekulatywnej ogólności. Czy kluczowe elementy oprogramowania powinny być otwarte do rozszerzenia? Tak. Wszystko? Nie. To samo w sobie jest spekulatywną ogólnością.
Jeśli chodzi o część zamkniętą, przechodząc z wersji 2.0 na 2.1 do 2.2 na 2.3 niektórych produktów, niezmodyfikowanie zachowania jest bardzo dobrym pomysłem. Użytkownikom naprawdę się to nie podoba, gdy każda niewielka wersja łamie własny kod. Jednak po drodze często stwierdza się, że początkowa implementacja w wersji 2.0 została zasadniczo złamana lub że ograniczenia zewnętrzne, które ograniczały początkowy projekt, nie mają już zastosowania. Czy uśmiechasz się i nosisz go i utrzymujesz ten projekt w wersji 3.0, czy też czynisz wersję 3.0 niekompatybilną pod jakimś względem? Kompatybilność wsteczna może być ogromnym ograniczeniem. Główne granice wydań są miejscem, w którym dopuszcza się łamanie wstecznej kompatybilności. Trzeba uważać, że może to spowodować zdenerwowanie użytkowników. Konieczne jest uzasadnienie, dlaczego konieczne jest zerwanie z przeszłością.
źródło
Refaktoryzacja z definicji zmienia strukturę kodu bez zmiany zachowania. A więc, gdy dokonujesz refaktoryzacji, nie dodajesz nowych funkcji.
To, co zrobiłeś jako przykład dla zasady Open Close, brzmi OK. Ta zasada dotyczy rozszerzenia istniejącego kodu o nowe funkcje.
Nie zrozum jednak źle tej odpowiedzi. Nie sugeruję, że powinieneś robić tylko funkcje lub refaktoryzować tylko duże fragmenty danych. Najczęstszym sposobem programowania jest wykonanie trochę funkcji niż natychmiastowe wykonanie refaktoryzacji (w połączeniu z testami, oczywiście, aby upewnić się, że nie zmieniłeś żadnego zachowania). Złożone refaktoryzacja nie oznacza „dużego” refaktoryzacji, oznacza stosowanie skomplikowanych i dobrze przemyślanych technik refaktoryzacji.
O zasadach SOLID. Są to naprawdę dobre wytyczne dotyczące tworzenia oprogramowania, ale nie są to religijne reguły, których należy ślepo przestrzegać. Czasami wiele razy, po dodaniu drugiej, trzeciej i n-tej funkcji, zdajesz sobie sprawę, że twój początkowy projekt, nawet jeśli szanuje Open-Close, nie jest zgodny z innymi zasadami lub wymaganiami oprogramowania. Ewolucja projektu i oprogramowania wymaga pewnych bardziej złożonych zmian. Chodzi o to, aby jak najszybciej znaleźć i zrealizować te problemy i jak najlepiej zastosować techniki refaktoryzacji.
Nie ma czegoś takiego jak idealny design. Nie ma takiego projektu, który mógłby i powinien być zgodny z wszystkimi istniejącymi zasadami lub wzorami. To jest utopia kodowania.
Mam nadzieję, że ta odpowiedź pomogła ci w twoim dylemacie. W razie potrzeby możesz poprosić o wyjaśnienia.
źródło
B
a kiedy będzie gotowa, zastąp starą implementacjęA
nową implementacjąB
(to jedno użycie interfejsów).A
Kod może służyć jako podstawa dlaB
kodu, a następnie mogę używać refaktoryzacjiB
kodu podczas jego tworzenia, ale myślę, że już przetestowanyA
kod powinien pozostać zamrożony.B
jest zbudowany na kodzieA
jako ewolucjaA
,B
to kiedy zostanie wydany,A
powinien zostać usunięty i nigdy więcej nie użyty. Klienci, którzy wcześniej używali,A
będą po prostu korzystaćB
bez wiedzy o zmianie, ponieważ interfejsI
nie został zmieniony (może trochę Zasada PodstawieniaZgodnie z moim rozumieniem - jeśli dodasz nowe metody do istniejącej klasy, nie spowoduje to uszkodzenia OCP. jestem jednak trochę pomylony z dodaniem nowych zmiennych w klasie. Ale jeśli zmienisz istniejącą metodę i parametry w istniejącej metodzie, to z pewnością zepsuje OCP, ponieważ kod jest już przetestowany i przekazany, jeśli celowo zmienimy metodę [Gdy zmiana wymagań], to będzie problem.
źródło