Тільки те, що система є складною, не означає, що ви повинні зробити її складною . Якщо у вас клас із занадто великою кількістю залежностей (або Співробітників) на кшталт цього:
public class MyAwesomeClass {
public class MyAwesomeClass(IDependency1 _d1, IDependency2 _d2, ... , IDependency20 _d20) {
// Assign it all
}
}
... тоді це стало занадто складним, і ви насправді не стежите за SRP , чи не так? Я ставлюся під заклад, якщо ви записували те, що MyAwesomeClass
робиться на картці CRC, це не вміщується на індексній картці, або ви повинні писати справді крихітні нерозбірливі літери.
Тут у вас є те, що ваші хлопці лише дотримувались принципу розбиття інтерфейсів, і, можливо, це довели до крайності, але це вже зовсім інша історія. Ви можете стверджувати, що залежності - це доменні об'єкти (що трапляється), однак наявність класу, який одночасно обробляє 20 об’єктів домену, розтягує його трохи надто далеко.
TDD дасть вам хороший показник того, наскільки клас працює. Тупо кажучи; якщо метод тестування має код налаштування, який потрібно писати назавжди (навіть якщо ви перефактуруєте тести), MyAwesomeClass
можливо, у вас є занадто багато чого робити.
То як ти вирішиш цю загадку? Ви переносите свої обов'язки на інші класи. Ви можете зробити кілька кроків для класу, який має цю проблему:
- Визначте всі дії (чи обов'язки), які робить ваш клас із залежностями.
- Групуйте дії відповідно до тісно пов'язаних залежностей.
- Повторний випуск! Тобто рефактор кожного з виявлених дій або до нових, або (що ще важливіше) інших класів.
Абстрактний приклад відповідальності рефакторингу
Нехай C
буде клас , який має кілька залежностей D1
, D2
, D3
, D4
що вам потрібно реорганізувати , щоб використовувати менше. Коли ми визначаємо, якими методами C
викликає залежність, ми можемо скласти простий її список:
D1
- performA(D2)
,performB()
D2
- performD(D1)
D3
- performE()
D4
- performF(D3)
Дивлячись на список, ми можемо побачити це D1
і D2
пов'язані між собою, оскільки клас хоч якось потребує їх разом. Ми також можемо побачити, що D4
потрібно D3
. Отже, у нас є дві групи:
Group 1
- D1
<->D2
Group 2
- D4
->D3
Групування - це показник того, що зараз клас має два обов'язки.
Group 1
- Один для обробки виклику двох об'єктів, які потребують один одного. Можливо, ви можете дозволити своєму класу C
усунути необхідність поводження з обома залежностями та залишити один із них обробляти ці виклики. У цій групуванні очевидно, що це D1
може мати посилання на D2
.
Group 2
- Інша відповідальність потребує одного об'єкта для виклику іншого. Ви не можете D4
впоратися D3
замість вашого класу? Тоді ми, мабуть, можемо виключити D3
з класу C
, дозволивши D4
робити дзвінки замість цього.
Не сприймайте мою відповідь як встановлену в камені, тому що приклад дуже абстрактний і робить багато припущень. Я впевнений, що існує більше способів відновити це, але, принаймні, ці кроки можуть допомогти вам отримати якийсь процес переміщення обов'язків, а не розділяти класи.
Редагувати:
Серед коментарів @Emmad Karem говорить:
"Якщо ваш клас має 20 параметрів у конструкторі, це не здається, що ваша команда зовсім знає, що таке SRP. Якщо у вас клас, який робить лише одне, то як він має 20 залежностей?" - Я думаю, що якщо ви мати клас клієнта, не дивно мати 20 параметрів у конструкторі.
Це правда, що для об'єктів DAO, як правило, є багато параметрів, які ви повинні встановити у своєму конструкторі, а параметри, як правило, є простими типами, такими як рядок. Однак у прикладі Customer
класу ви все ще можете згрупувати його властивості всередині інших класів, щоб зробити речі простішими. Такі , як має Address
клас з вулиць і Zipcode
клас , який містить поштовий індекс і буде обробляти бізнес - логіку , такі як перевірка достовірності даних , а також:
public class Address {
private String street1;
//...
private Zipcode zipcode;
// easy to extend
public bool isValid() {
return zipcode.isValid();
}
}
public class Zipcode {
private string zipcode;
public bool isValid() {
// return regex match that zipcode contains numbers
}
}
Ця річ обговорюється далі у публікації блогу "Ніколи, ніколи, ніколи не використовуй String на Java (або принаймні часто)" . В якості альтернативи використання конструкторів або статичних методів для полегшення створення під об’єктів ви можете використовувати шаблон побудови рідини .