Ось проблема, з якою я часто стикаюся: Нехай буде проект веб-магазину, який має клас продукту. Я хочу додати функцію, яка дозволяє користувачам розміщувати відгуки на продукт. Тож у мене є клас Огляд, який посилається на товар. Тепер мені потрібен метод, в якому перераховані всі відгуки до товару. Є дві можливості:
(А)
public class Product {
...
public Collection<Review> getReviews() {...}
}
(B)
public class Review {
...
static public Collection<Review> forProduct( Product product ) {...}
}
Подивившись на код, я вибрав би (A): він не статичний і йому не потрібен параметр. Однак я відчуваю, що (A) порушує Принцип єдиної відповідальності (SRP) та Принцип відкритого закриття (OCP), тоді як (B) не:
(SRP) Коли я хочу змінити спосіб збирання відгуків для продукту, я повинен змінити клас продукту. Але має бути лише одна причина, чому потрібно змінити клас продукту. І це точно не відгуки. Якщо я упакую кожну функцію, яка має щось спільне з продуктами в Продукті, вона незабаром буде забита.
(OCP) Я повинен змінити клас продукту, щоб розширити його за допомогою цієї функції. Я думаю, що це порушує частину принципу "Закрито для змін". Перш ніж я отримав запит замовника на реалізацію оглядів, я вважав Продукт готовим і "закрив" його.
Що важливіше: слідувати принципам SOLID або мати більш простий інтерфейс?
Або я взагалі щось не так роблю?
Результат
О, дякую за всі ваші чудові відповіді! Важко вибрати одну як офіційну відповідь.
Дозвольте підсумувати основні аргументи з відповідей:
- pro (A): OCP не є законом і читабельність коду також має значення.
- pro (A): відносини сутності повинні бути судноплавними. Обидва класи можуть знати про цей взаємозв'язок.
- pro (A) + (B): зробіть і те, і делегуйте в (A) - (B), щоб виріб мінявся повторно.
- pro (C): покладіть методи пошуку в третій клас (сервіс) там, де він не є статичним.
- contra (B): перешкоджає глузуванню під час тестів.
Кілька додаткових речей, які сприяли мої коледжі на роботі:
- pro (B): наша рамка ORM може автоматично генерувати код для (B).
- pro (A): з технічних причин нашої системи ORM потрібно буде в деяких випадках змінити "закритий" об'єкт, незалежно від того, куди шукає шукач. Тому я не завжди зможу дотримуватися SOLID.
- contra (C): сильно метушиться ;-)
Висновок
Я використовую обидва (A) + (B) з делегацією для мого поточного проекту. Однак у середовищі, орієнтованій на сервіс, я піду з (C).
Assert(5 = Math.Abs(-5));
Abs()
не є проблемою, тестування чогось, що від цього залежить. У вас немає шва для ізоляції залежного коду під тестом (CUT), щоб використовувати макет. Це означає, що ви не можете перевірити його як атомну одиницю, і всі ваші тести стають інтеграційними тестами, що керуються логікою тестового блоку. Невдача в тесті може бути в CUT або в Abs()
(або його залежному коді) і усуває переваги діагностики одиничних тестів.