Według Roberta C. Martina SRP stwierdza, że:
Klasa nigdy nie powinna mieć więcej niż jednego powodu do zmiany.
Jednak w swojej książce Clean Code , rozdział 3: Funkcje, pokazuje następujący blok kodu:
public Money calculatePay(Employee e) throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
A następnie stwierdza:
Istnieje kilka problemów z tą funkcją. Po pierwsze, jest duży, a po dodaniu nowych typów pracowników wzrośnie. Po drugie, bardzo wyraźnie robi więcej niż jedną rzecz. Po trzecie, narusza zasadę pojedynczej odpowiedzialności (SRP), ponieważ istnieje więcej niż jeden powód, aby ją zmienić . [moje podkreślenie]
Po pierwsze, myślałem, że SRP został zdefiniowany dla klas, ale okazuje się, że ma on również zastosowanie do funkcji. Po drugie, w jaki sposób ta funkcja ma więcej niż jeden powód do zmiany ? Widzę, że zmienia się tylko z powodu zmiany Pracownika.
Odpowiedzi:
Jednym często pomijanym szczegółem zasady jednolitej odpowiedzialności jest to, że „przyczyny zmiany” są pogrupowane według aktorów przypadków użycia (pełne wyjaśnienie znajduje się tutaj ).
Tak więc, w twoim przykładzie,
calculatePay
metoda będzie musiała zostać zmieniona za każdym razem, gdy wymagane będą nowe typy pracowników. Ponieważ jeden typ pracownika może nie mieć nic wspólnego z innym, trzymanie ich razem byłoby pogwałceniem zasady, ponieważ zmiana wpłynęłaby na różne grupy użytkowników (lub aktorów przypadków użycia) w systemie.Teraz, czy zasada ma zastosowanie do funkcji: Nawet jeśli masz naruszenie tylko w jednej metodzie, nadal zmieniasz klasę z więcej niż jednego powodu, więc nadal jest to naruszenie SRP.
źródło
Na stronie 176, rozdział 12: Pojawienie się, w części zatytułowanej Minimalne klasy i metody książka zawiera pewną korektę, stwierdzając:
i
Oczywiście, on mówi o dogmatyzmie w podążaniu za SRP, aby rozbić doskonale niewinne małe metody takie jak
calculatePay()
powyżej.źródło
Kiedy Martin stosuje SRP do funkcji, domyślnie rozszerza swoją definicję SRP. Ponieważ SRP jest sformułowaniem ogólnej zasady specyficznym dla OO, a ponieważ jest dobrym pomysłem, gdy stosuje się go do funkcji, nie widzę z tym problemu (chociaż mogłoby być miło, gdyby wyraźnie zawarł go w definicja).
Nie widzę też więcej niż jednego powodu do zmiany i nie wierzę, że myślenie o SRP w kategoriach „obowiązków” lub „powodów do zmiany” jest pomocne. Zasadniczo SRP ma na myśli to, że jednostki programowe (funkcje, klasy itp.) Powinny zrobić jedną rzecz i zrobić to dobrze.
Jeśli spojrzysz na moją definicję, nie jest ona mniej ogólnikowa niż zwykłe sformułowanie SRP. Problem ze zwykłymi definicjami SRP nie polega na tym, że są one zbyt niejasne, ale że starają się być zbyt szczegółowe w odniesieniu do czegoś, co jest zasadniczo niejasne.
Jeśli spojrzysz na to
calculatePay
, co robi, wyraźnie robi to jedno: wysyłka na podstawie typu. Ponieważ Java ma wbudowane sposoby przeprowadzania wysyłki opartej na typach,calculatePay
jest nieelegancka i nie jest idiomatyczna, dlatego należy ją przepisać, ale nie z podanych powodów.źródło
Masz rację @Enrique. Bez względu na to, czy jest to funkcja czy metoda klasy, SRP oznacza, że w tym bloku kodu wykonujesz tylko jedną rzecz.
Oświadczenie „powód zmiany” jest czasem nieco mylące, ale jeśli zmienisz
calculateSalariedPay
lubcalculateHourlyPay
wytryskEmployee.type
musisz zmienić tę metodę.W twoim przykładzie funkcja:
Moim zdaniem nie jest to bezpośrednie naruszenie SRP, ponieważ nie można napisać krótszych przypadków i połączeń, jeśli myślisz o pracowniku i metody już istnieją. W każdym razie jest to wyraźne naruszenie zasady otwartego zamknięcia (OCP), ponieważ musisz dodać oświadczenia „przypadku”, jeśli dodajesz typy pracowników, więc jest to zła implementacja: przerób to.
Nie wiemy, jak należy obliczać „Pieniądze”, ale najłatwiejszym sposobem jest posiadanie
Employee
interfejsu i konkretnych implementacjigetMoney
metod. W takim przypadku cała funkcja jest niepotrzebna.Jeśli obliczenie jest bardziej skomplikowane, można użyć wzorca odwiedzającego, który również nie jest w 100% SRP, ale jest bardziej OCP niż skrzynka przełączników.
źródło