Що краще: купа гетерів або 1 метод з параметром рядка вибору?


15

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

Існує кілька розрахунків, які необхідно виконати за даними стопи.

Тепер, який API краще:

class Foot : public RecognizedObject  { 
  MaxPressureFrame getMaxPressureFrame();
  FootAxis getFootAxis();
  AnatomicalZones getAnatomicalZones();

  // + similar getters for other calculations

  // ...
}

Або:

class Foot : public RecognizedObject {
  virtual CalculationBase getCalculation(QString aName);

  // ...
}

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

Будь-яка порада?

Деякі профі для першого підходу можуть бути:

  • KISS - все дуже конкретно. API, але також і реалізація.
  • сильно набрані повернені значення.
  • успадковування від цього класу є нерозумним. Нічого не можна відміняти, лише додавати.
  • API дуже закритий, нічого не входить, нічого не можна відміняти, тому менше може піти не так.

Деякі конфлікти:

  • Кількість отримувачів буде зростати, оскільки кожен новий розрахунок, який ми вигадуємо, додається до списку
  • API швидше змінюється, і якщо будуть внесені порушення, нам потрібна нова версія API, Foot2.
  • у разі повторного використання класу в інших проектах, можливо, нам не знадобиться кожен розрахунок

Деякі профі для другого підходу:

  • більш гнучким
  • api рідше зміниться (якщо припустити, що абстракція правильна, якщо ні, зміна обійдеться дорожче)

Деякі конфлікти:

  • вільно набрано. Потреби формуються під час кожного дзвінка.
  • параметр string - у мене погані почуття з цього приводу (розгалуження на значення рядків ...)
  • Немає жодного випадку / вимоги використання, яка б надавала додаткову гнучкість, але це може бути в майбутньому.
  • API встановлює обмеження: кожен обчислення має виходити з базового класу. Отримати обчислення буде вимушено за допомогою цього методу 1, і передача додаткових параметрів буде неможливою, якщо тільки ми не розробимо ще більш динамічний, надто гнучкий спосіб передачі параметрів, який ще більше збільшує складність.

5
Ви можете зробити enumі ввімкнути його значення. І все-таки я вважаю, що другий варіант є злим, оскільки він відхиляється від KISS.
Vorac

Важче знайти всі звичаї певного обчислення, якщо для його запуску використовується параметр рядка. Важко бути 100%, ти їх усіх знайшов.
Конрад Моравський

Візьміть найкраще з двох світів і напишіть фасад із купою геттерів getCalculation().
nalply

1
Енуми, безумовно, краще, ніж струнні! Я не думав про це. Вони обмежують API і запобігають зловживанню параметром рядка (наприклад, concat жетонів та інших лайнів). Тому я здогадуюсь порівняння між варіантом 1 та варіантом 2 із enum замість рядка.
Bgie

Відповіді:


6

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

  • Кількість отримувачів буде зростати, оскільки кожен новий розрахунок, який ми вигадуємо, додається до списку

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

  • API швидше змінюється, і якщо будуть внесені порушення, нам потрібна нова версія API, Foot2.

Правда, але це насправді величезна проблема;) ви можете визначити інтерфейси для часткових API, і тоді вам не потрібно повторно компілювати залежний клас, на який не впливають новіші API (Тому немає потреби у Foot2). Це дозволяє краще роз'єднати, залежність тепер залежить від інтерфейсу, а не від реалізації. Більше того, якщо існуючий інтерфейс зміниться, ви будете мати помилку компіляції у залежних класах, що чудово запобігає застарілому коду.

  • у разі повторного використання класу в інших проектах, можливо, нам не знадобиться кожен розрахунок

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


Мені подобається часткова ідея API з інтерфейсами, це здається непрозорим. Я піду з тим. Дякую. Якщо вона стане занадто захаращеною (стопа реалізує занадто багато інтерфейсів), використання декількох малих класів адаптерів було б ще більш гнучким: Якщо існує декілька варіацій ступні з різними api (наприклад, стопа, людська стопа, собачий стоп, manfootVersion2), може бути невеликий адаптер для кожного, щоб дозволити одному віджету GUI працювати з усіма ними ...
Bgie

Перевагою використання селектора команд є те, що можна мати реалізацію, яка отримує команду, яка не розуміє виклик статичного допоміжного методу, наданого з інтерфейсом. Якщо команда являє собою щось, що можна зробити майже з усіма реалізаціями за допомогою підходу загального призначення, але які деякі реалізації можуть бути в змозі зробити за допомогою кращих засобів [врахуйте, наприклад IEnumerable<T>.Count], такий підхід може дозволити коду користуватися перевагами продуктивності нових функції інтерфейсу при використанні реалізацій, які їх підтримують, але залишаються сумісними зі старими реалізаціями.
supercat

12

Я б рекомендував варіант 3: Дайте зрозуміти, що обчислення не є сутнісною частиною абстракції а Foot, але оперують нею. Потім ви можете розділити Footі обчислення на окремі класи, наприклад:

class Foot : public RecognizedObject {
public:
    // Rather low-level API to access all characteristics that might be needed by a calculation
};

class MaxPressureFrame {
public:
    MaxPressureFrame(const Foot& aFoot); // Performs the calculation based on the information in aFoot
    //API for accessing the results of the calculation
};

// Similar classes for other calculations

Таким чином, у вас все ще сильно вводиться ваш розрахунок, і ви можете додавати нові, не впливаючи на існуючий код (якщо ви не зробили серйозної помилки в інформації про кількість, яку викрили Foot).


Однозначно приємніше, ніж варіанти 1 і 2. Це лише короткий крок від використання схеми розробки стратегії.
Брайан

4
Це може бути доречно. Це може бути зайвим. Залежить від того, наскільки складні розрахунки. Ви збираєтесь додати MaxPressureFrameStrategy та MaxPressureFrameStrategyFactory? І воно, як правило, перетворює Стопу в анемічний об'єкт.
user949300

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

@Bgie: З такими залежностями між розрахунками я погоджуюся з @ user116462, що перший варіант найкращий ".
Барт ван Інген Шенау
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.