Термін (або "шаблон"?) Для "Зробити щось, якщо це вже не зроблено" [закрито]


54

Я знаю, що це дуже просто, я знаю, але нещодавно колега сказав мені, що названий метод startHttpServerє надто складним, щоб зрозуміти, оскільки він запускає сервер лише тоді, коли він вже не працює. Я вважаю, що я потрапляю в проблеми, коли відповідаю: "Серйозно? Я займаюся цим десятиліттями - це звичайна модель програмування". Частіше, ніж мені байдуже визнати, він повертається з деякими задокументованими доказами, які свідчать про те, що вся спільнота програмування відстає від його точки зору, і я в кінцевому підсумку відчуваю себе дурним.

Запитання : Чи існує задокументована модель дизайну, що стоїть за концепцією методу, який не відповідає, якщо необхідні дії вже є на ділі? Або, якщо не візерунок, чи має це ім’я? А якщо ні, то чи є причина, щоб вважати, що надто складно розглянути можливість написання методу таким чином?


6
звучить як кешування - і це часто вважається складною (див. також Як я поясню $ {щось} до $ {когось? )
gnat

8
Ви отримали 3 різні відповіді, але найкращою відповіддю було б застосувати їх усі: перейменуйте оригінальну функцію, розділіть її код на дві функції (де одна з двох тепер отримує назву startHttpServer), і так, тут застосовується термін "idempotent" Ну.
Док Браун

8
Який вид зустрічних доказів надає ваш колега? Чи можете ви надати посилання на нього?
Роберт Харві

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

4
Я думаю, що кращим способом подумати над питанням "Зроби щось, якщо цього ще не зроблено" було б "Покласти систему на цей стан". Звичайно, якщо система вже перебуває у такому стані, метод нічого не зробить - якою була б очікувана поведінка.
Т. Сар - Відновлення Моніки

Відповіді:


127

Як вже сказав NickWilliams : поняття, яке описує ОП, називається idempotent (іменник Idempotency ). Це дійсно звичайна практика, особливо в API високого рівня.

АЛЕ: Перейменуйте функцію.

Замість того , щоб startHttpServerназвати його makeSureHttpServerIsRunningабо ensureHttpServerIsRunning.

Коли викликається функція startHttpServer, читачі очікують, що вона запустить HTTP-сервер; коли мене дзвонять десять разів поспіль, у мене буде працювати десять серверів. Ваша функція не робить це більшість часу. Додатково, назва "start" підказує, що якщо я хочу лише один сервер, який потрібно працювати, мені доведеться відслідковувати, чи функція вже викликана чи ні.

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


83
Це. Особисто я замість цього використовую "Забезпечити". Справа в тому, що це має стати схемою називання, яку розуміють у вашій команді.
Ейфорія

3
або почати кидати виняток, якщо його вже запущено. як sqlconnection.open
Еван

9
Я завжди використовую функцію "забезпечити" для "виконувати, якщо вже не".
Каз

3
Ще одне ім’я, яке я часто бачу, - це getOrCreateSomething, які створюються лише під час першого дзвінка, а потім просто повертають щось
Fabich

5
@aroth Ви впевнені, що не очікуєте, що він спробує знайти будь-який доступний порт і повернути його? Або просто погано написано? ;)
jpmc26

33

Перейменуйте його на EnsureServerRunning.

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

(Альтернатива StartServerIfNotRunning:?)


1
Більшість абонентів просто дбає про те, що сервер працює після дзвінка. Як це досягається, їм все одно.
gnasher729

29

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

Ідентичні методи. Методи також можуть мати властивість "idempotence", оскільки (окрім помилок чи проблем із закінченням терміну дії) побічні ефекти N> 0 однакових запитів такі ж, як і для одного запиту. ( Від W3.org )

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

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


5
Функція не є ідентичною, якщо викликати її один раз запускає http-сервер, а виклик вдруге не означає. Це буквально протилежне самопочуттю. Було б ідентично, якби кожен виклик запускав новий http-сервер.
Полігном

36
@Polygnome Wikipedia визначає ідентифікацію як f (f (x)) = f (x), що загалом відповідає опису Ніка. Тут функція введення та виведення буде непрямим станом сервера, тому стан «сервер працює» був би фіксованою точкою для цієї функції. Можливо, я тут неправильно розумію статтю Вікіпедії, чи можете ви посилатися на інші посилання?
амон

16
@ Polygnome: ця відповідь правильна, idempotency відноситься до функцій, для яких неважливо, називаються вони один раз або більше одного разу, результат залишається завжди однаковим. Тут результат - одна запущена послуга http, незалежно від кількості викликів функції.
Док Браун

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

10
@ Polygnome Вибачте, ви помиляєтесь. Idempotent означає, що запит має той самий ефект, незалежно від того, чи виконується він один раз чи більше одного разу. Запуск N http-серверів, якщо отримано N дублікатів запиту, точно не є ідентичним.
Каз

7

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

Логіка функції

Технічно, шляхом розщеплення startHttpServer«s логіка в 2 -х функцій і називаючи їх по окремо , все , що ви робите це переміщення startHttpServer » s ідемпотентність в код виклику обидві функції замість ... Крім того, якщо ви не звернути як логіку в третьої функції (що робить startHttpServerпо-перше), це змушує вас писати код unDRYed, дублюючи його експоненціально скрізь, де вам доведеться зателефонувати startHttpServer. Коротше кажучи, startHttpServer доводиться називати себе isHttpServerRunningфункцією.

Отже, моя думка:

  • isHttpServerRunningФункція реалізації, оскільки це може знадобитися незалежно в будь-якому випадку ...
  • Реалізуйте, startHttpServerвикористовуючи його isHttpServerRunningдля визначення відповідної наступної дії ...

Тим не менш, ви можете startHttpServerповернути будь-яке значення, яке може знадобитися користувачеві цієї функції, наприклад:

  • 0 => помилка запуску сервера
  • 1 => сервер починає успіх
  • 2 => сервер вже запущений

Іменування функції

Перш за все, яка головна мета користувача? Щоб запустити сервер HTTP , правда?

По суті, немає жодної проблеми, маючи намір почати щось, що вже було розпочато, AKA 1*1=1. Тож, принаймні, мені називати це " ensureHttpServerIsRunning" видається не критично необхідним, я б більше піклувався про те, як довго, природно і запам'ятовується ім'я функції.

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

Ви дізнаєтесь функцію один раз, коли ви пишете її кілька разів ...

Так що я все-таки дотримуюся цього, startHttpServerякий коротший, простіший і чіткіший, ніж ensureHttpServerIsRunning.


1
Тим більше, що метод повинен приймати деякі аргументи конфігурації, такі як кореневий каталог, налаштування безпеки, можливо номер порту, я на 100% комфортний з "запуску" тут.
user949300

@ user949300: Користувач, який викликає "secureHttpServerIsRunning", не переймається конфігурацією, кореневим каталогом тощо. Це справа людини, яка його реалізує.
gnasher729

2
@ gnasher729 Це передбачає, що http-сервер є Singleton. Однодонці - це зло. І, можливо, тут недоречно. Можна легко мати декілька серверів у декількох портах. Якщо існує справді лише один http-сервер, то весь цей метод, IMO, поганий дизайн. Краще просто запустити сервер один раз при ініціалізації програми.
user949300

2

Я вважаю, що ваш колега мав на увазі, що startHttpServerробить занадто багато:

  • Перевірка, чи вже працює сервер,
  • Запуск сервера при необхідності.

Це дві непов'язані частини коду. Наприклад, подібна ситуація існує, коли настільний додаток повинен переконатися, що він не працює під час запуску; буде частина коду, який обробляє екземпляри програми (наприклад, за допомогою mutex), і код, який запустить цикл повідомлень програми.

Це означає, що ви повинні мати не один, а принаймні¹ два методи :

  • isHttpServerRunning: boolean
  • startHttpServer

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


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


21
Тепер щось інше, що називає ці дві функції, - це дві речі, плюс, оголення функцій окремо може ввести умови перегонів. Без безкоштовного обіду.
whatsisname

1
Якщо це занадто багато для колеги, удачі.
gnasher729

@ gnasher729: ця відповідь не суперечить вашій - навпаки, справді може бути хорошим ідеєю поєднати перейменування методу з розділенням його на два. І поки ми не бачимо реального коду, ми не знаємо, наскільки складний код.
Док Браун

10
Ця відповідь, здається, протистоїть абстракції та сухості. Що робити, якщо startHttpServerв коді називається більше одного місця? Чи слід скрізь склеювати кілька подібних рядків? Чи слід це робити з усіма функціями? Досить скоро у вас програма буде нескінченного розміру.
ЖакБ

3
Я думаю, що перший побічний ефект такого підходу полягає в тому, що початок startHttpServerметоду буде виглядати приблизно так if (isHttpServerRunning()){ return; }. Ви стверджуєте бізнес-правило, що "невірно запускати http-сервер, якщо він вже запущений", але потім покладаєш на нього відповідальність за виконання цього правила. Тимчасові та неодноразово в усіх місцях, де вони можуть зателефонувати startHttpServer.
aroth

2

Оскільки ви не вказуєте мову, у JavaScript багато бібліотек мають функцію "раз", наприклад, підкреслення . Отже, якщо це було б вам знайоме, назвіть це «раз» і, можливо, перейменуйте свій метод.

Сам, приходячи більше від Java, приходять на думку терміни "кешування" або "лінива оцінка". "Ідемпотент" є технічно правильним і хорошим вибором, особливо. якщо у вас є більш функціональне походження.


Це може бути не один раз, якщо сервер можна зупинити.
gnasher729

@ gnasher729. Я міг уявити рідко використовуваний restartHttpServer()метод. Але просто зупиняючись - в чому корисний випадок? Вам подобаються спорадичні збої в з’єднанні? :-)
user949300

Не потрібно мати випадок використання для просто зупинки сервера HTTP, оскільки його, можливо, зупинило щось зовнішнє для самої програми - сервер HTTP, можливо, відхилився і помер, адміністратор, можливо, вручну зупинив його, пр.
Дейв Шерохман

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

-2

Я вважаю за краще startHttpServerIfNotIsRunning.

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


Я припускаю, що ти не носій мови? Тому що забезпечити чітке визначення того, для чого ми йдемо. Але все-таки справедливим моментом є те, що документація та API не повинні вимагати того, щоб рівень мови носія англійської мови був зрозумілим.
Voo

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

2
@Voo - я носій мови, і я не впевнений, що означає забезпечення.
Джонатан У ролях

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

@jcast: Серйозно?
gnasher729

-3

Те, що ваш колега мав би сказати вам, це те, що у вас немає ділового написання цього методу. Це вже написано багато разів, і написано краще, ніж ви, ймовірно, це напишете. Наприклад: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

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


2
Мова - Java, і метод був розроблений для запуску грізлі вбудованого сервера в автономному додатку Джерсі. Я не можу помилитися в точності вашого коментаря, враховуючи, що я не дав вам багато контексту, але ви багато чого припускали, роблячи свою заяву. Цілком чудово мати метод, який запускає приструн або грізлі для запуску сервера.
Джон Калькот

1
Є мільйони бізнес-додатків, які включають самостійний розміщений HTTP-сервер для надання API. І всім їм знадобиться дуже простий код, який фактично налаштовує бібліотеку, яку вони використовують, і надає інформацію, таку як інтерфейс, який потрібно зв’язати, який порт використовувати, налаштування безпеки тощо. startServerФункція чи подібне - це все, крім рідкісного. . Це не означає, що ви запишете деталі з низьким рівнем зернистості.
Voo
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.