Карта функцій проти оператора switch


21

Я працюю над проектом, який обробляє запити, і до цього запиту є два компоненти: команда та параметри. Обробник для кожної команди дуже простий (<10 рядків, часто <5). Є щонайменше 20 команд, і, ймовірно, буде більше 50.

Я придумав пару рішень:

  • один великий перемикач / інше на команди
  • карта команд до функцій
  • карта команд для статичних класів / синглтон

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

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

Я придумав наступний список про / кон для кожного:

перемикач

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

команди map -> функція

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

команди на карті -> статичний клас / синглтон

  • плюси
    • може використовувати поліморфізм для обробки простої перевірки помилок (лише як 3 рядки, але все ж)
    • подібні переваги для рішення карти -> функцій
  • мінуси
    • багато дуже маленьких класів будуть захаращувати проект
    • реалізація не все в одному місці, тому сканувати реалізацію не так просто

Додаткові примітки:

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

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

Reply Command(List<String> params)

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


8
відображати команди для функцій та завантажувати їх під час виконання з конфігурації. використовувати шаблон команди.
Стівен Еверс

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

8
Що з цим світом, що випробовується, де люди відповідають у запитаннях у коментарях та запитують більше інформації у відповідях?
пдр

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

1
@pdr - Правильно. Моя схильність була карта команд до функцій, але я відносно молодший програміст в курсі дизайну CS. Мій професор любить багато занять, тому є принаймні 2 законних рішення. Я хотів дізнатись улюбленого спільноти.
beatgammit

Відповіді:


14

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

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

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

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


4

Ваша проблема дуже добре піддається дизайну дизайну Command . Отже, у вас буде базовий Commandінтерфейс, і тоді буде кілька CommandImplкласів, які реалізували б цей інтерфейс. По суті, інтерфейс повинен просто мати єдиний метод doCommand(Args args). Ви можете передавати аргументи через екземпляр Argsкласу. Таким чином ви використовуєте силу поліморфізму замість незграбних тверджень if / else. Також ця конструкція легко розширюється.


3

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

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

І навпаки, якщо ви використовуєте поліморфізм у стилі OO, легко додавати нові випадки (просто складіть новий клас), але важко додати методи в інтерфейс (адже тоді вам потрібно повернутися і відредагувати купу класів)

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


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

Наприклад, ви можете використовувати делегування, щоб зробити частину перевірки помилок замість того, щоб покладатися на спадщину (мій приклад - у Javascript, тому що O не знаю синтаксису Go, я сподіваюся, ви не заперечуєте)

function make_command(real_command){
    return function(x){
        if(check_arguments(x)){
            return real_command(x);
        }else{
            //handle error here
        }
    }
 }

 command1 = make_command(function(x){ 
     //do something
 })

 command2 = make_command(function(x){
     //do something else
 })

 command1(17);
 commnad2(42);

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

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


1

Я ніколи не використовував go, тому що програміст ac # я, мабуть, пішов би наступним рядком, сподіваюся, ця архітектура буде відповідати тому, що ви робите.

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

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

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


0

Як вибираєте свої команди / функції?

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

Крім того, тестування окремих функцій простіше в ізоляції, ніж величезна заява про перемикання.

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


Команди - це унікальні рядки. Я можу зіставити їх на цілі числа, якщо потрібно.
beatgammit

0

Я поняття не маю, як працює Go, але архітектура, яку я використовував у ActionScript, - це наявність списку, що поєднується вдвічі, який виступає ланцюгом відповідальності. Кожне посилання має функцію deterResponsibility (яку я реалізував як зворотній дзвінок, але ви можете написати кожне посилання окремо, якщо це краще працює для того, що ви намагаєтеся зробити). Якщо посилання визначить, що воно несе відповідальність, воно викличе meetResponsibility (знову ж таки зворотний виклик), і це припинить ланцюг. Якщо вона не несе відповідальності, вона передасть запит на наступне посилання в ланцюжку.

Різні випадки використання можна легко додати та вилучити, просто додавши новий зв’язок між ланками (або в кінці) існуючого ланцюга.

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

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