Мені доводиться йти на компроміс: DRY або Command-Query-Separation?


10

Нещодавно я переробляв метод, який був і командним, і методом запитів.

Розділивши його на один метод команд та один метод запиту, я виявив, що в коді зараз є кілька місць, куди я викликаю команду, а потім отримую значення з запиту, що здається порушенням принципу DRY.

Але якби я перетворив цей загальний код на метод, то цей метод був би і командою, і запитом. Це прийнятно?


гаразд, я не знав, чи спільнота домовляється, і я не міг знайти жодної дискусії на цю тему.
Kris Welsh

Його частіше називають CQRS google.com.au/…
Даніель Літтл

@DanielLittle - ні, це не так. CQS і CQRS є різними предметами. CQRS є набагато більш задіяною архітектурною схемою, тоді як CQS - це більше дизайнерський зразок і набагато простіше зрозуміти та реалізувати. Дивіться codebetter.com/gregyoung/2009/08/13/command-query-separation
Ерік Функенбуш

@Erik Funkenbusch Ти маєш рацію
Даніель Літтл

Відповіді:


11

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

QueryResult command()
{
   // do command stuff
   return query();
}

4

Я раніше не чув про розділення команд-запитів (CQS), але, схоже, це стосувалося б принципу єдиної відповідальності (SRP), де зазначено, що функція / клас в ідеалі повинна відповідати за виконання однієї і однієї тільки речі .

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

Однак, ідучи з вашим гіпотетичним прикладом, я, швидше за все, створив би метод обгортки, який поєднав би вашу команду та запит, щоб DRY не порушувався в численних місцях коду. Я також не вважав би це порушенням SRP (а може бути, і CQS), оскільки обгортка все ще несе лише одну відповідальність: поєднати команду із запитом та створити абстракцію вищого рівня, яку простіше споживати.

Я думаю, що метод обгортки - цілком прийнятне рішення, і щоб проілюструвати це, давайте зробимо ваш приклад ще на крок. Що робити, якщо вам довелося запустити 2 запити замість 1, а потім виконати командну дію на основі цього. Отже, у ваших 2 рядках коду було б 6 або 8. Що робити, якщо між однією та іншою були деякі перевірки / перевірка даних, то тепер у вас є 15 рядків коду. Чи подумаєте ви двічі над створенням обгортки, яка б все це робила, а не розсипала ці 15 рядків у декілька файлів?


Я думаю, що "єдиним принципом" обгортки повинно бути збереження інших методів, які потребують команди та запиту разом DRY.
Droogans


Хоча рішення цієї проблеми Карл краще, я вважаю, що ваша розробка довгих функцій обгортки є дуже гарним моментом.
Kris Welsh

-3

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

CQS - це відповідь на труднощі мов, які не підтримують відстеження ефектів, розуміння коду, який виконується як за його результатами, так і за його ефектами. Однак:

  1. Неможливо уникнути необхідності виконання коду для його результатів, оскільки це основа для складання великих програм з малих одиниць.

  2. Неможливо також уникнути необхідності виконання коду для його ефектів, оскільки поза математикою та теоретичною інформатикою значення запуску програми лежить на тому, що вона очевидно може зробити для нас.

  3. Необхідності викликати ефекти та створювати результати в одному і тому ж коді неможливо уникнути, оскільки на практиці нам потрібні як ефекти, так і композиційність, а не лише той чи інший.

Дійсно вирішити проблему відстеження наслідків, що є надто важким для людей, які не мають допомоги, - це, звичайно, те, щоб комп'ютери допомагали нам людям ! Аналогічну річ можна сказати і про відстеження складних зв’язків між значеннями часу виконання (наприклад, дійсністю індексів масиву), для яких винятки та контракти, що виконуються під час виконання, становлять (не) рішення.

На закінчення, такі рішення, як CQS, просто перешкоджають розробці програм відповідно до здорових принципів, заснованих на реальності. Перейдіть на DRY.


Іноді вам потрібно уникати з'єднання, щоб зменшити складність. Слід поглянути на CQRS.
Даніель Літтл

@Lavinski: Найкращим інструментом уникнення складності (не зменшення її, тобто марного) є абстракція - розв'язування родової суті проблем, які ми вирішуємо, від конкретних деталей прикладів зазначених родових задач. Магічні рецепти (або "дизайнерські зразки", як я чую, їх називають) у кращому випадку можуть запобігти нанесенню занадто великої шкоди, коли ви неправильно зробите дизайн, але вони не можуть перетворити неправильний дизайн у правильний.
піон

@Lavinski: Концептуально правильне альтернативне рішення стосується конкретно CQRS: 1. зрозумійте модель даних (жодна кількість шарів об'єктів не може усунути необхідність цього), 2. кодувати стільки властивостей правильності, скільки ви можете на схемі бази даних. (На жаль, більшість популярних RDBMS надають досить обмежену підтримку останнього, не кажучи вже про NoSQL, які отримують це ще більше помилок. Мої поточні дослідження пропонують краще рішення для цього.)
pyon

CQRS працює повністю відповідно до дизайну, керованого доменом. Я пропоную вам трохи дослідити. Домен всередині програми повинен застосовувати правильність, а не ваш сховище даних.
Даніель Літтл

1
@ EduardoLeón: Якщо ви хочете довести, що ваш дизайн правильний, спробуйте написати тести для вашої програми. Я можу вам гарантувати, що викидання CQS лише завадить вашим зусиллям у цьому.
Стефан Біллієт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.