Zasada pojedynczej odpowiedzialności jest zdefiniowana w Wikipedii jako
Zasada pojedynczej odpowiedzialności jest zasadą programowania komputerowego, która stwierdza, że każdy moduł, klasa lub funkcja powinna ponosić odpowiedzialność za jedną część funkcjonalności zapewnianej przez oprogramowanie, a odpowiedzialność powinna być całkowicie zamknięta w klasie
Jeśli klasa powinna ponosić tylko jedną odpowiedzialność, jak może mieć więcej niż jedną metodę? Czy każda metoda nie miałaby innej odpowiedzialności, co oznaczałoby, że klasa miałaby więcej niż 1 odpowiedzialność.
Każdy przykład, który widziałem demonstrując zasadę pojedynczej odpowiedzialności, wykorzystuje przykładową klasę, która ma tylko jedną metodę. Pomocne może być zobaczenie przykładu lub wyjaśnienie klasy za pomocą wielu metod, które nadal można uznać za jedną odpowiedzialność.
Odpowiedzi:
Jedna odpowiedzialność może nie być czymś, co może spełnić jedna funkcja.
Ta klasa może złamać zasadę jednej odpowiedzialności. Nie dlatego, że ma dwie funkcje, ale jeśli koduje
getX()
igetY()
musi zadowolić różnych interesariuszy, którzy mogą wymagać zmian. Jeśli wiceprezydent Pan X rozsyła notatkę, że wszystkie liczby powinny być wyrażone jako liczby zmiennoprzecinkowe, a dyrektor ds. Rachunkowości pani Y nalega, aby wszystkie liczby, które ocenia jej departament, pozostały liczbami całkowitymi, niezależnie od tego, co pan X uważa za dobre, to ta klasa powinna mieć jeden pomysł na to, za kogo jest on odpowiedzialny, ponieważ sprawy stają się mylące.Gdyby przestrzegano SRP, byłoby jasne, czy klasa Lokacji przyczynia się do rzeczy, na które narażony jest Pan X i jego grupa. Wyjaśnij, za co klasa jest odpowiedzialna, a wiesz, która dyrektywa wpływa na tę klasę. Jeśli oba wpływają na tę klasę, to została źle zaprojektowana, aby zminimalizować wpływ zmiany. „Klasa powinna mieć tylko jeden powód do zmiany” nie oznacza, że cała klasa może zrobić tylko jedną małą rzecz. Oznacza to, że nie powinienem być w stanie spojrzeć na klasę i powiedzieć, że zarówno pan X, jak i pani Y są zainteresowani tą klasą.
Inne niż takie rzeczy. Nie, wiele metod jest w porządku. Po prostu nadaj mu nazwę, która wyjaśnia, które metody należą do klasy, a które nie.
SRP wuja Boba bardziej dotyczy prawa Conwaya niż prawa Curly'ego . Wujek Bob opowiada się za zastosowaniem prawa Curly'ego (zrób jedną rzecz) do funkcji, a nie klas. SRP przestrzega przed mieszaniem powodów do wspólnej zmiany. Prawo Conwaya mówi, że system będzie śledził przepływ informacji w organizacji. To prowadzi do przestrzegania SRP, ponieważ nie obchodzi Cię to, o czym nigdy nie słyszysz.
Ludzie wciąż chcą, aby SRP dotyczyło każdego powodu ograniczenia zakresu. Jest więcej powodów do ograniczenia zakresu niż SRP. Ponadto ograniczam zakres, nalegając, aby klasa była abstrakcją, która może przyjąć nazwę zapewniającą, że zaglądanie do środka nie zaskoczy Cię .
Możesz zastosować Prawo Curly'ego do zajęć. Jesteś poza tym, o czym mówi wujek Bob, ale możesz to zrobić. Jeśli pomylisz się, zaczniesz myśleć, że oznacza to jedną funkcję. To tak, jakby myśleć, że rodzina powinna mieć tylko jedno dziecko. Posiadanie więcej niż jednego dziecka nie powstrzymuje go od bycia rodziną.
Jeśli zastosujesz prawo Curly'ego do klasy, wszystko w klasie powinno dotyczyć jednej idei jednoczącej. Ten pomysł może być szeroki. Chodzi o wytrwałość. Jeśli są tam jakieś funkcje rejestrowania, są one wyraźnie nie na miejscu. Nie ma znaczenia, czy pan X jest jedynym, który dba o ten kod.
Klasyczna zasada, którą należy tu zastosować, nazywa się oddzieleniem obaw . Jeśli oddzielisz wszystkie swoje obawy, można argumentować, że to, co zostało w jednym miejscu, jest jednym z nich. Tak nazwaliśmy ten pomysł, zanim film City Slickers z 1991 roku przedstawił nam postać Curly.
To jest w porządku. Po prostu to, co wujek Bob nazywa odpowiedzialnością, nie jest problemem. Na nim nie spoczywa odpowiedzialność. To coś, co może zmusić cię do zmiany. Możesz skupić się na jednym problemie i nadal tworzyć kod, który będzie odpowiedzialny przed różnymi grupami ludzi o różnych programach.
Może nie obchodzi cię to. W porządku. Myślenie, że trzymanie się „robienia jednej rzeczy” rozwiąże wszystkie wasze problemy projektowe, pokazuje brak wyobrażenia o tym, czym może być „jedna rzecz”. Innym powodem ograniczenia zakresu jest organizacja. Możesz zagnieździć wiele „jednej rzeczy” w innych „jednej rzeczy”, dopóki nie pojawi się szuflada śmieci pełna wszystkiego. Mówiłem o tym wcześniej
Oczywiście klasycznym powodem OOP do ograniczenia zakresu jest to, że klasa ma w nim prywatne pola, a zamiast tego używają getterów do dzielenia się tymi danymi, umieszczamy każdą metodę, która potrzebuje tych danych w klasie, w której mogą używać danych prywatnie. Wielu uważa, że jest to zbyt restrykcyjne, aby używać go jako ogranicznika zakresu, ponieważ nie każda metoda, która należy do siebie, używa dokładnie tych samych pól. Chciałbym zapewnić, że jakikolwiek pomysł, który połączył dane, był tym samym pomysłem, który połączył metody.
Sposób funkcjonalny spojrzeć na to, że
a.f(x)
ia.g(x)
po prostu f a (x) i g (x). Nie dwie funkcje, ale kontinuum par funkcji, które się różnią. Nie musi nawet zawierać danych. Może to być po prostu jak wiesz, który i realizacja masz zamiar użyć. Funkcje, które zmieniają się razem, należą do siebie. To dobry stary polimorfizm.a
f
g
SRP jest tylko jednym z wielu powodów ograniczenia zakresu. Ten jest dobry. Ale nie jedyny.
źródło
Kluczem tutaj jest zakres lub, jeśli wolisz, szczegółowość . Część funkcjonalności reprezentowana przez klasę można dalej podzielić na części funkcjonalności, przy czym każda część jest metodą.
Oto przykład. Wyobraź sobie, że musisz utworzyć plik CSV z sekwencji. Jeśli chcesz być zgodny z RFC 4180, wdrożenie algorytmu i obsłużenie wszystkich przypadków brzegowych zajmie trochę czasu.
Wykonanie tego w jednej metodzie spowodowałoby kod, który nie będzie szczególnie czytelny, a zwłaszcza metoda zrobiłaby kilka rzeczy na raz. Dlatego podzielisz go na kilka metod; na przykład jeden z nich może być odpowiedzialny za generowanie nagłówka, tj. pierwszej linii CSV, podczas gdy inna metoda przekształciłaby wartość dowolnego typu na jego reprezentację ciągu odpowiednią dla formatu CSV, podczas gdy inna określiłaby, czy wartość należy ująć w podwójne cudzysłowy.
Metody te mają własną odpowiedzialność. Metoda, która sprawdza, czy istnieje potrzeba dodania podwójnych cudzysłowów, ma swoją własną, a metoda, która generuje nagłówek, ma jedną. Jest to SRP stosowane do metod .
Teraz wszystkie te metody mają jeden wspólny cel, to jest wykonanie sekwencji i wygenerowanie CSV. Jest to jedyna odpowiedzialność klasy .
Pablo H skomentował:
W rzeczy samej. Podany przeze mnie przykład CSV ma jedną metodę publiczną, a wszystkie pozostałe są prywatne. Lepszym przykładem może być kolejka zaimplementowana przez
Queue
klasę. Ta klasa zawierałaby w zasadzie dwie metody:push
(również wywoływanaenqueue
) ipop
(również wywoływanadequeue
).Obowiązkiem
Queue.push
jest dodanie obiektu do ogona kolejki.Obowiązkiem
Queue.pop
jest usunięcie obiektu z głowy kolejki i obsłużenie przypadku, gdy kolejka jest pusta.Obowiązkiem
Queue
klasy jest zapewnienie logiki kolejki.źródło
Funkcja jest funkcją.
Odpowiedzialność to odpowiedzialność.
Mechanik odpowiada za naprawę samochodów, co będzie wiązało się z diagnostyką, kilkoma prostymi zadaniami konserwacyjnymi, niektórymi faktycznymi naprawami, niektórymi przekazaniem zadań innym itp.
Klasa kontenera (lista, tablica, słownik, mapa itp.) Jest odpowiedzialna za przechowywanie obiektów, co obejmuje przechowywanie ich, umożliwianie wstawiania, zapewniania dostępu, pewnego rodzaju porządkowania itp.
Pojedyncza odpowiedzialność nie oznacza, że jest bardzo mało kodu / funkcjonalności, oznacza to, że każda funkcjonalność „należy do siebie” pod tą samą odpowiedzialnością.
źródło
Jedna odpowiedzialność niekoniecznie oznacza, że robi tylko jedną rzecz.
Weźmy na przykład klasę usług użytkownika:
Ta klasa ma wiele metod, ale jej odpowiedzialność jest jasna. Zapewnia dostęp do rekordów użytkowników w magazynie danych. Jedyne zależności to model użytkownika i magazyn danych. Jest luźno sprzężony i bardzo spójny, a tak naprawdę SRP próbuje sprawić, byś pomyślał.
SRP nie należy mylić z „zasadą segregacji interfejsów” (patrz SOLID ). Zasada segregacji interfejsów (ISP) mówi, że mniejsze, lekkie interfejsy są lepsze niż większe, bardziej uogólnione interfejsy. Go intensywnie wykorzystuje ISP w swojej standardowej bibliotece:
SRP i ISP są z pewnością powiązane, ale jedno nie implikuje drugiego. ISP jest na poziomie interfejsu, a SRP na poziomie klasy. Jeśli klasa implementuje kilka prostych interfejsów, może nie mieć już tylko jednej odpowiedzialności.
Podziękowania dla Luaana za wskazanie różnicy między ISP a SRP.
źródło
UserService
iUser
być UpperCamelCase, ale metodyCreate
,Exists
iUpdate
bym się lowerCamelCase.W restauracji jest szef kuchni. Jego jedynym obowiązkiem jest gotować. Ale potrafi gotować steki, ziemniaki, brokuły i setki innych rzeczy. Czy zatrudniłbyś jednego szefa kuchni na danie w swoim menu? A może jeden szef kuchni na każdy składnik każdego dania? Lub jeden szef kuchni, który może spełnić swoją jedyną odpowiedzialność: gotować?
Jeśli poprosisz szefa kuchni o wykonanie listy płac, wtedy naruszasz SRP.
źródło
Kontrprzykład: przechowywanie stanu zmiennego.
Załóżmy, że miałeś najprostszą klasę w historii, której jedynym zadaniem jest przechowywanie
int
.Jeśli jesteś ograniczony tylko do 1 metody, możesz mieć albo
setState()
, albogetState()
, chyba że złamiesz enkapsulację ii
upublicznisz.Tak wyraźnie, ta pojedyncza odpowiedzialność wymaga posiadania co najmniej 2 metod w tej klasie. CO BYŁO DO OKAZANIA.
źródło
Źle interpretujesz zasadę pojedynczej odpowiedzialności.
Pojedyncza odpowiedzialność nie oznacza jednej metody. Oznaczają różne rzeczy. W rozwoju oprogramowania mówimy o spójności . Funkcje (metody) charakteryzujące się wysoką spójnością „należą” do siebie i mogą być liczone jako wykonywanie pojedynczej odpowiedzialności.
Projektant systemu musi zaprojektować system w taki sposób, aby zasada pojedynczej odpowiedzialności była spełniona. Można to postrzegać jako technikę abstrakcji i dlatego czasami jest to kwestia opinii. Wdrożenie zasady pojedynczej odpowiedzialności sprawia, że kod jest łatwiejszy do przetestowania i łatwiejszy do zrozumienia jego architektury i projektu.
źródło
Często pomocne jest (w dowolnym języku, ale szczególnie w językach OO) patrzeć na rzeczy i organizować je z punktu widzenia danych, a nie funkcji.
Dlatego należy rozważyć odpowiedzialność klasy za utrzymanie integralności i zapewnienie pomocy w prawidłowym wykorzystaniu danych, które posiada. Oczywiście jest to łatwiejsze, jeśli cały kod jest w jednej klasie, niż rozłożony na kilka klas. Dodanie dwóch punktów jest bardziej niezawodne, a kod łatwiejszy w utrzymaniu, dzięki
Point add(Point p)
metodzie wPoint
klasie niż w innym miejscu.W szczególności klasa nie powinna ujawniać niczego, co mogłoby skutkować niespójnymi lub niepoprawnymi danymi. Na przykład, jeśli
Point
musi znajdować się w płaszczyźnie (0,0) do (127, 127), konstruktor i wszelkie metody, które modyfikują lub produkują nowyPoint
mają obowiązek sprawdzania podanych wartości i odrzucania wszelkich zmian, które mogłyby to naruszać wymaganie. (Często coś takiegoPoint
byłoby niezmienne, a upewnienie się, że nie ma żadnych sposobów modyfikacjiPoint
po jego zbudowaniu, byłoby również odpowiedzialnością klasy)Pamiętaj, że warstwowanie tutaj jest całkowicie dopuszczalne. Możesz mieć
Point
klasę do radzenia sobie z poszczególnymi punktami iPolygon
klasę do radzenia sobie z zestawemPoint
s; nadal mają one osobne obowiązki, ponieważPolygon
przekazują całą odpowiedzialność za radzenie sobie z czymkolwiek wyłącznie związanym zPoint
(na przykład za zapewnienie, że punkt ma zarówno wartość, jakx
iy
wartość)Point
klasie.źródło