Виписки рефакторингу і чи існує взагалі реальна користь для виписок?


28

Я читав цю статтю і цікавився, чи ми можемо позбутися всіх операторів комутації, замінивши їх на Словник чи Фабрику, щоб у моїх проектах взагалі не було заяв про перемикання.

Щось не дуже склалося.

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


10
Якщо припустити, що ви реалізуєте завод, як ви вирішите, який тип об’єкта створити?
CodeART


@CodeWorks: Я, звичайно, десь матиму умови, вирішуючи, яку конкретну реалізацію використовувати.
Каніні

@CodeWorks: З віртуальним конструктором, очевидно. (І якщо ви не зможете реалізувати заводський зразок таким чином, вам потрібна краща мова.)
Мейсон Уілер

Відповіді:


44

І switchтвердження, і поліморфізм мають своє вживання. Зауважте, що існує й третя опція (мовами, які підтримують функції покажчиків / лямбдатів та функцій вищого порядку): відображення відповідних ідентифікаторів до функцій обробника. Це доступно, наприклад, на C, що не є мовою ОО, і C #, що є *, але немає (поки що) в Java, що є і OO *.

У деяких процедурних мовах (не маючи поліморфізму та функцій вищого порядку) switch/ if-elseзаяви були єдиним способом вирішення класу проблем. Тому багато розробників, звикши до такого способу мислення, продовжували використовувати switchнавіть мови OO, де поліморфізм часто є кращим рішенням. Ось чому часто рекомендується уникати / рефакторних switchтверджень на користь поліморфізму.

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

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

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

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

* Я тут дуже вільно використовую термін "ОО"; Мене не цікавлять концептуальні дебати щодо того, що є "реальним" чи "чистим" ОО.


4
+1. Крім того, ці рекомендації, як правило, сформульовані: " віддайте перевагу поліморфізму, щоб переключитися", і це хороший вибір слова, дуже відповідає цій відповіді. Він визнає, що існують обставини, коли відповідальний кодер може зробити інший вибір.
Карл Манастер

1
І бувають випадки, коли ви хочете підходити до комутації, навіть якщо у коді є їх мільйон. Я маю на увазі фрагмент коду, який генерує 3D-лабіринт. Використання пам’яті збільшилось би, якби однобайтові комірки в масиві були замінені класами.
Лорен Печтель

14

Тут я повертаюся до динозавра ...

Висловлювання комутаторів не є поганими самі по собі, його використання, яке зроблено з них, викликає сумніви.

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

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

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

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

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


4
У мене був викладач, який сказав, що в ідеалі слід писати код, "щоб хтось, хто нічого не знав про програмування (як, можливо, ваша мати), міг це зрозуміти". На жаль, це ідеал, але, можливо, ми повинні все-таки включити наших мам до оглядів коду!
Майкл К

+1 для "але якщо ви потрапили на один рядок коду, де вам потрібно додати щось нове, вам доведеться перестрибувати всюди, щоб розробити те, що потрібно додати / змінити"
швидко_віть

8

Переклади-заяви проти підтипу-поліморфізму є давньою проблемою і часто згадуються у спільноті FP в дискусіях щодо проблеми вираження .

В основному ми маємо типи (класи) та функції (методи). Як ми кодуємо матеріал, щоб легко додавати нові типи чи нові методи останнього?

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

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

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


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

Ні. Такі абсолюти рідко є доброю ідеєю.

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


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

@ZanLynx, правда повна протилежність, принаймні, з C #. Якщо ви подивитеся на IL, створений для нетривіального оператора перемикання, ви побачите, що компілятор перетворює його на словник. Це швидше.
Девід Арно

@DavidArno: "повна протилежність"? Те, як я це прочитав, ми сказали абсолютно те саме. Як це може бути навпаки?
Зан Лінкс

2

Бачачи, як це мова-агностик, пропускний код найкраще працює з switchоператорами:

switch(something) {
   case 1:
      foo();
   case 2:
      bar();
      baz();
      break;

   case 3:
      bang();
   default:
      bizzap();
      break;
}

Еквівалент з if, з дуже незручним випадку по замовчуванням. І майте на увазі, що чим більше можливого проходження, тим довший список умов стане:

if (1 == something) {
   foo();
}
if (1 == something || 2 == something) {
   bar();
   baz();
}
if (3 == something) {
   bang();
}
if (1 != something && 2 != something) {
   bizzap();
}

(Однак, зважаючи на те, що кажуть деякі інші відповіді, я відчуваю, що я пропустив суть питання ...)


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

1

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

tl; dr - вони корисні, але досить часто ви можете зробити краще.


2
Я пахну деякими упередженнями - чому поліморфізм більш елегантний, ніж відповідність візерунка? І що змушує вас вважати, що поліморфізм не існує у FP?
тдаммери

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

Ви забуваєте про поліморфізм через класи та інстанції.
тдаммери

Ви коли-небудь чули про виразну проблему ? OO і FP краще в різних ситуаціях, якщо ви перестанете думати про це.
хугомг

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