Zasada jednolitej odpowiedzialności oparta jest na zasadzie wysokiej spójności. Różnica między nimi polega na tym, że bardzo spójne klasy zawierają zestaw ściśle powiązanych ze sobą obowiązków, podczas gdy klasy przestrzegające SRP mają tylko jedną odpowiedzialność.
Ale w jaki sposób ustalamy, czy dana klasa ma zestaw obowiązków i dlatego jest po prostu bardzo spójna, czy też ma tylko jedną odpowiedzialność i w ten sposób przestrzega SRP? Innymi słowy, czy nie jest to mniej lub bardziej subiektywne, ponieważ niektórzy mogą uznać klasę za bardzo szczegółową (i jako tacy będą wierzyć, że klasa przestrzega SRP), podczas gdy inni mogą uznać ją za niewystarczająco szczegółową?
design-patterns
single-responsibility
cohesion
użytkownik1483278
źródło
źródło
Odpowiedzi:
Dlaczego tak, jest to bardzo subiektywne i jest przedmiotem wielu gorących debat o czerwonych twarzach, w które angażują się programiści.
Tak naprawdę nie ma jednej odpowiedzi, która może się zmienić w miarę, jak oprogramowanie staje się bardziej złożone. To, co kiedyś było pojedynczym, dobrze zdefiniowanym zadaniem, może ostatecznie stać się wieloma źle zdefiniowanymi zadaniami. To zawsze jest pocieranie. Jak wybrać właściwy sposób podziału programu na zadania?
Jedyną radą, jaką mogę udzielić, jest taka: skorzystaj z własnego osądu (i twoich współpracowników). I pamiętaj, że błędy można (zwykle) skorygować, jeśli złapiesz je wkrótce.
źródło
Bob Martin (wujek Bob), który stworzył SOLIDNE zasady, których SRP jest pierwszy, mówi o tym (parafrazuję, nie pamiętam faktycznych słów):
Jeśli ma więcej niż jeden powód, nie przestrzega SRP.
źródło
Mogę podać kilka praktycznych zasad.
źródło
Zasady pojedynczej odpowiedzialności mówią, że każdy moduł oprogramowania powinien mieć tylko jeden powód do zmiany. W ostatnim artykule wujek Bob wyjaśnił „powód do zmiany”,
Następnie wyjaśnił tę koncepcję na przykładzie TUTAJ .
źródło
Aby odpowiedzieć na to pytanie, cofnij się o krok i zastanów się nad intencją zasady pojedynczej odpowiedzialności. Dlaczego przede wszystkim jest to zalecana zasada projektowania?
Celem tej zasady jest „podzielenie na przedziały” podstawy kodu, więc kod odnoszący się do pojedynczej „odpowiedzialności” jest izolowany w jednej jednostce. Ułatwia to znalezienie i zrozumienie kodu, a co ważniejsze, oznacza, że zmiany w „odpowiedzialności” wpłyną tylko na jedną jednostkę kodu.
W systemie absolutnie nigdy nie chcesz, gdy jedna mała szansa powoduje awarię lub zmianę zachowań innej pozornie niezwiązanej części kodu. SRP pomaga izolować błędy i zmiany.
Czym zatem jest „odpowiedzialność”? Jest to coś, co można zmienić niezależnie od innych zmian. Załóżmy, że masz program, który może zapisać niektóre ustawienia w pliku konfiguracyjnym XML i może odczytać je z tego pliku. Czy jest to jedna odpowiedzialność, czy też „ładowanie” i „oszczędzanie” to dwie różne obowiązki? Każda zmiana formatu lub struktury pliku wymagałaby zmiany logiki ładowania i zapisu. Jest to zatem jedna odpowiedzialność, którą powinna reprezentować jedna klasa. Teraz rozważ aplikację, która może eksportować niektóre dane w formacie CVS, Excel i XML. W takim przypadku łatwo sobie wyobrazić, że jeden format może się zmienić bez wpływu na drugi. Jeśli zdecydujesz się zmienić separator w formacie CVS, nie powinno to wpłynąć na wynik programu Excel.
źródło
OO mówi, że klasy to grupowanie danych funkcjonalność. Ta definicja pozostawia wiele miejsca na subiektywną interpretację.
Wiemy, że klasy powinny być jasno i łatwo definiowane. Ale aby zdefiniować taką klasę, musimy mieć jasne pojęcie, w jaki sposób klasa pasuje do ogólnego projektu. Bez wymagań typu wodospadu, które paradoksalnie są uważane za anty-wzór ... jest to trudne do osiągnięcia.
Możemy wdrożyć projekt klasy z architekturą, która działa w większości przypadków, takich jak MVC. W aplikacjach MVC zakładamy, że mamy tylko dane, interfejs użytkownika i wymóg komunikacji między nimi.
Dzięki podstawowej architekturze łatwiej jest zidentyfikować przypadki, w których łamane są zasady pojedynczej odpowiedzialności. EG Przekazywanie wystąpienia kontroli użytkownika do modalu.
źródło
Dla celów dyskusji przywołam klasę JUCE o nazwie AudioSampleBuffer . Teraz ta klasa istnieje, aby przechowywać fragment dźwięku (a może raczej długi fragment) dźwięku. Wie, że liczba kanałów, liczba próbek (na kanał) wydaje się być przypisana do 32-bitowej liczby zmiennoprzecinkowej IEEE, zamiast mieć zmienną reprezentację liczbową lub wielkość słów (ale to nie jest ze mną problem). Istnieją funkcje składowe, które pozwalają uzyskać numChannels lub numSamples i wskaźniki do dowolnego konkretnego kanału. Możesz zwiększyć lub zmniejszyć AudioSampleBuffer. Zakładam, że poprzednie zero-pads bufor, podczas gdy drugi obcina.
Istnieje kilka prywatnych członków tej klasy, które są używane do przydzielania miejsca na specjalnej stercie używanej przez JUCE.
Ale tego właśnie brakuje AudioSampleBuffer (i miałem na ten temat kilka dyskusji z Jules): członek o nazwie
SampleRate
. Jak mogło tego brakować?Jedynym obowiązkiem, który AudioSampleBuffer musi spełnić, jest odpowiednie odwzorowanie fizycznego dźwięku, który słyszy, że reprezentują jego próbki. Po wprowadzeniu AudioSampleBuffer z czegoś, co czyta plik dźwiękowy lub ze strumienia, musisz uzyskać dodatkowy parametr i przekazać go wraz z AudioSampleBuffer do metod przetwarzania (powiedzmy, że jest to filtr), który musi znać częstotliwość próbkowania lub, ostatecznie do metody, która odtwarza bufor, aby go usłyszeć (lub przesyła go strumieniowo do innego miejsca). Cokolwiek.
Ale musisz nadal przekazywać ten parametr SampleRate, który jest nieodłączny od specyficznego dźwięku w AudioSampleBuffer, wszędzie wokół. Widziałem kod, w którym stała funkcji 44100.0f została przekazana do funkcji, ponieważ programista chyba nie wiedział, co jeszcze zrobić.
Jest to przykład niewywiązania się z pojedynczej odpowiedzialności.
źródło
Można zrobić konkretny sposób, w oparciu o to, co powiedziałeś - że wysoka spójność pociąga za sobą pojedynczą odpowiedzialność, którą możesz zmierzyć spójność. Maksymalna spójna klasa ma wszystkie pola używane we wszystkich metodach. Chociaż maksymalna spójna klasa nie zawsze jest możliwa lub pożądana, nadal najlepiej jest do niej dotrzeć. Mając ten cel projektowania klas, łatwo jest wywnioskować, że twoja klasa nie może mieć wielu metod lub pól (niektórzy twierdzą najwyżej 7).
Innym sposobem jest czysta podstawa OOP - model po prawdziwych obiektach. O wiele łatwiej jest dostrzec odpowiedzialność prawdziwych obiektów. Jeśli jednak prawdziwy obiekt jest zbyt złożony, podziel go na wiele obiektów zawierających, każdy z nich ma swoją własną odpowiedzialność.
źródło