Чи застосовується Принцип єдиної відповідальності щодо функцій?


17

За словами Роберта К. Мартіна, СРП стверджує, що:

Ніколи не повинно бути більше ніж одна причина для зміни класу .

Однак у своїй книзі « Чистий код» , глава 3: Функції, він показує такий блок коду:

    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);
        }
    }

А потім заявляє:

Існує кілька проблем з цією функцією. По-перше, він великий, а коли будуть додані нові типи працівників, він буде рости. По-друге, це дуже чітко робить більше ніж одне. По-третє, він порушує Принцип єдиної відповідальності (СРП), оскільки існує декілька причин для його зміни . [акцент мій]

По-перше, я думав, що SRP визначений для класів, але виявляється, це також застосовно до функцій. По-друге, як така функція має декілька причин для зміни ? Я бачу, як це змінюється лише через зміну працівника.


5
Це виглядає як випадок із підручника щодо поліморфізму.
wchargin

Це дуже цікава тема. Чи є шанс додати наступне рішення цієї проблеми? Я б сумнівався в тому, що можна покласти в кожний клас працівника коефіцієнт калькуляції, але це було б погано, тому що кожен клас працівника може бути змінений через: 1. Розрахункові платежі. 2. додавання більше властивостей до класу тощо.
Став Алфі

Відповіді:


13

Однією часто пропущеною деталлю Принципу єдиної відповідальності є те, що "причини змін" групуються за суб'єктами, що застосовують справи (ви можете ознайомитися з повним поясненням тут ).

Отже, у вашому прикладі calculatePayметод потрібно буде змінювати, коли потрібні нові типи працівників. Оскільки один тип співробітників може не мати нічого спільного з іншим, це було б порушенням принципу, якщо ви тримаєте їх разом, оскільки зміна вплине на різні групи користувачів (або суб’єкти використання випадків) у системі.

Тепер про те, чи застосовується принцип до функцій: Навіть якщо у вас є порушення лише в одному методі, ви все одно змінюєте клас з більш ніж однієї причини, тому це все-таки порушення SRP.


1
Я спробував переглянути пов’язане відео на YouTube, але через 10 хвилин у ньому, не згадуючи про групування за акторами, що використовуються, я відмовився. Перші 6 хвилин суперечать ентропії без видимих ​​причин. Якщо ви дасте місце у відео, де він починає обговорювати це, було б корисно.
Майкл Шоу

@MichaelShaw Спробуйте переглядати з 10:40. Дядько Боб згадує, що код "зміниться з різних причин через різних людей". Я думаю, що це може бути те, на що Мішель Генріх намагається вказати нам.
Енріке

Завершив перегляд 50-хвилинного відео на YouTube, більшість з яких не стосувалося того, що слід було уточнити. Я помітив о 16:00, що він уже перейшов з цієї теми, і він ніколи не повертався до неї. "Пояснення" по суті зводиться до цього: "відповідальність" в SRP не означає, що це означає "різні причини змін", що насправді означає "зміни на прохання різних людей", що насправді означає "зміни в запит різних ролей, які грають люди ". "Пояснення" нічого не пояснює, воно замінює одне розпливчасте слово іншим.
Майкл Шоу

2
@MichaelShaw, як у цитаті з книги, якщо вам потрібно ввести різні типи працівників, вам доведеться змінити клас Співробітник. Різні ролі можуть нести відповідальність за виплату різних типів працівників, тому в цьому випадку клас Співробітник повинен бути змінений на більш ніж одну роль, отже, порушення SRP.
MichelHenrich

1
@MichaelShaw так, ти маєш рацію - SRP залежить від того, як організація організована. Саме тому я додаю "може" або "може" до всіх своїх коментарів :). Однак навіть у тих випадках, хоча код може не порушувати SRP, він, безумовно, порушує OCP.
MichelHenrich

3

На сторінці 176, Глава 12: Надзвичайна ситуація, у розділі під назвою Мінімальні класи та методи книги дещо виправлено, зазначивши:

Прагнучи зробити наші класи та методи невеликими, ми могли б створити занадто багато крихітних класів та методів. Таким чином, це правило дозволяє припустити, що ми також підтримуємо низькі функції та кількість класів

і

Високий рівень класів та методів іноді є результатом безглуздого догматизму.

Очевидно, що він говорить про догматизм у дотриманні СРП, щоб зруйнувати ідеально дрібні невинні маленькі методи, як calculatePay()вище.


3

Коли містер Мартін застосовує SRP до функції, він неявно розширює своє визначення SRP. Оскільки SRP є специфічним для OO формулюванням загального принципу, і оскільки це гарна ідея при застосуванні до функцій, я не бачу з цим проблеми (хоча це, можливо, було б непогано, якби він явно включив її до визначення).

Я не бачу більше однієї причини змінитись, і не вірю, що думка про СРП з точки зору "обов'язків" або "причин для зміни" є корисною. По суті, те, що SRP отримує, це те, що програмні об'єкти (функції, класи тощо) повинні робити одне і робити це добре.

Якщо ви поглянете на моє визначення, воно не менш розпливчасте, ніж звичайне формулювання СРП. Проблема звичайних визначень СРП полягає не в тому, що вони занадто розпливчасті, а в тому, що вони намагаються бути занадто конкретними щодо чогось суттєво розпливчастого.

Якщо ви подивитеся на те, що calculatePayробить, це однозначно робить одне: відправлення на основі типу. Оскільки у Java є вбудовані способи здійснення диспетчеризації на основі типу, calculatePayце неелегантно і не ідіоматично, тому її слід переписувати, але не з вказаних причин.


-2

Ви праві @Enrique. Незалежно від того, чи це функція чи метод класу, SRP означає, що в цьому кодовому блоці ви робите лише одне.

Заява "причина зміни" іноді трохи вводить в оману, але якщо ви змінюєте calculateSalariedPayабо calculateHourlyPayперелічуєте Employee.typeцей метод, ви повинні змінити цей метод.

У вашому прикладі функція:

  • перевіряє тип працівника
  • називає іншу функцію, яка розраховує гроші відповідно до типу

На мою думку, це не є безпосередньо порушенням SRP, оскільки випадки комутації та дзвінки не можуть бути написані коротше, якщо ви думаєте про Співробітника та методи вже існують. У будь-якому випадку це чітке порушення відкритого закритого принципу (OCP), оскільки ви повинні додавати заяви "case", якщо ви додаєте типи службовців, тому це погана реалізація: refactor.

Ми не знаємо, як слід розраховувати "Гроші", але найпростіший спосіб - це мати Employeeінтерфейс і деякі конкретні реалізації з getMoneyметодами. У цьому випадку вся функція непотрібна.

Якщо його складніше обчислити, можна використовувати шаблон відвідувача, який також не є 100% SRP, але це більше OCP, ніж вимикач.


2
Не впевнений, як ви можете перерахувати 2 речі, які функціонує, але скажіть, що це не порушення SRP.
JeffO

@JeffO: Це не 2 речі, це 2 частини однієї речі: виклик відповідної функції на основі типу.
Майкл Шоу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.