Czy zasada pojedynczej odpowiedzialności ma zastosowanie do funkcji?

17

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.

Enrique
źródło
5
To wygląda jak podręcznik do polimorfizmu.
wchargin
To bardzo interesujący temat. Czy istnieje szansa, że ​​dodasz następujące rozwiązanie tego problemu? Powiedziałbym, że umieściłem funkcję kalkulacyjną Pay w każdej klasie pracowników, ale byłoby to złe, ponieważ teraz każdą klasę pracowników można zmienić, ponieważ: 1. Obliczenia płatności 2. dodanie więcej właściwości do klasy itp.
Stav Alfi

Odpowiedzi:

13

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, calculatePaymetoda 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.

MichelHenrich
źródło
1
Próbowałem obejrzeć link do filmu na youtube, ale po 10 minutach bez wzmianki o grupowaniu według aktorów z przypadków użycia zrezygnowałem. Pierwsze 6 minut to gadanie o entropii, bez wyraźnego powodu. Jeśli podasz miejsce w filmie, w którym zacznie to omawiać, byłoby to pomocne.
Michael Shaw
@MichaelShaw Spróbuj oglądać od 10:40. Wujek Bob wspomina, że ​​kod „zmieni się z różnych powodów, z powodu różnych ludzi”. Wydaje mi się, że to właśnie MichelHenrich chce nam wskazać.
Enrique,
Skończyłem oglądać cały 50-minutowy film na youtube, z którego większość nie dotyczyła tego, co miał wyjaśnić. Zauważyłem o godzinie 16:00, że już przeszedł od tego tematu i nigdy do niego nie powrócił. „Wyjaśnienie” zasadniczo sprowadza się do tego: „odpowiedzialność” w SRP nie oznacza, że ​​oznacza „różne powody zmiany”, co tak naprawdę oznacza „zmiany na prośbę różnych osób”, co tak naprawdę oznacza „zmiany na prośba o różne role, które grają ludzie ". „Wyjaśnienie” niczego nie wyjaśnia, zastępuje jedno niejasne słowo innym.
Michael Shaw
2
@MichaelShaw, tak jak w cytacie z książki, jeśli chcesz wprowadzić różne typy pracowników, musisz zmienić klasę pracownika. Różne role mogą być odpowiedzialne za zapłatę różnych rodzajów pracowników, dlatego w tym przypadku klasa pracownika musi zostać zmieniona na więcej niż jedną rolę, stąd naruszenie SRP.
MichelHenrich,
1
@MichaelShaw tak, masz rację - SRP zależy od tego, jak organizacja jest zorganizowana. Właśnie dlatego do wszystkich moich komentarzy dodaję „może” lub „może” :). Jednak nawet w tych przypadkach, chociaż kod nie może naruszać SRP, z pewnością narusza OCP.
MichelHenrich,
3

Na stronie 176, rozdział 12: Pojawienie się, w części zatytułowanej Minimalne klasy i metody książka zawiera pewną korektę, stwierdzając:

Aby uczynić nasze klasy i metody małymi, możemy stworzyć zbyt wiele małych klas i metod. Zasada ta sugeruje więc, że utrzymujemy również naszą funkcję i liczbę klas na niskim poziomie

i

Wysoka liczba klas i metod jest czasem wynikiem bezsensownego dogmatyzmu.

Oczywiście, on mówi o dogmatyzmie w podążaniu za SRP, aby rozbić doskonale niewinne małe metody takie jak calculatePay()powyżej.

Mike Nakis
źródło
3

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, calculatePayjest nieelegancka i nie jest idiomatyczna, dlatego należy ją przepisać, ale nie z podanych powodów.

Michael Shaw
źródło
-2

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 calculateSalariedPaylub calculateHourlyPaywytrysk Employee.typemusisz zmienić tę metodę.

W twoim przykładzie funkcja:

  • sprawdza rodzaj pracownika
  • wywołuje inną funkcję, która oblicza pieniądze według rodzaju

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 Employeeinterfejsu 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.

H
źródło
2
Nie jestem pewien, jak można wymienić 2 funkcje, ale powiedz, że nie jest to naruszenie SRP.
JeffO,
@JeffO: To nie są 2 rzeczy, to 2 części jednej rzeczy: wywołanie odpowiedniej funkcji na podstawie typu.
Michael Shaw,