Термин (или «шаблон»?) Для «Сделай что-нибудь, если это еще не сделано» [закрыто]

54

Звучит довольно просто, я знаю, но недавно мой коллега сказал мне, что вызываемый метод startHttpServerслишком сложен для понимания, потому что он запускает сервер, только если он еще не запущен. Я обнаруживаю, что попадаю в неприятности, когда отвечаю: «Серьезно? Я занимаюсь этим десятилетиями - это обычная практика в программировании» Чаще, чем я хочу признаться, он возвращается с некоторыми документальными свидетельствами, которые показывают, что все сообщество программистов отстает от его точки зрения, и я в конечном итоге чувствую себя застенчивым.

Вопрос : Есть ли документированный шаблон проектирования, стоящий за концепцией метода, который является запретным, если требуемое действие уже действует? Или, если не шаблон, у него тоже есть имя? И если нет, есть ли причина думать, что слишком сложно подумать о написании метода таким образом?

Джон Калькот
источник
6
звучит как кэширование - и это часто считается осложненной действительно (смотрите также Как объяснить $ {что - то} до $ {кто}? )
комар
8
Вы получили 3 разных ответа, но лучший ответ был бы, примените их все: переименуйте исходную функцию, разделите ее код на две функции (где одна из двух теперь получает имя startHttpServer), и да, термин «идемпотент» применяется здесь Что ж.
Док Браун
8
Какого рода контраргументы предоставляет ваш коллега? Можете ли вы предоставить ссылку на него?
Роберт Харви
5
Мне любопытно посмотреть, откуда твой коллега приводит свои цитаты. По крайней мере, в отношении переполнения стека и разработки программного обеспечения эта функция была бы названа в большинстве случаев плохо, но не имела бы необычного поведения. Имейте в виду, что это не потому, что кто-то что-то разместил в блоге, так как это представляет собой взгляд всего сообщества программистов. Даже такие громкие имена, как Мартин Фаулер, время от времени говорят очень странные вещи. Мы все просто люди.
Т. Сар - Восстановить Монику
4
Я думаю, что лучший способ подумать о «Сделай что-нибудь, если это еще не сделано» - это «Поставить систему в это состояние». Конечно, если система уже находится в этом состоянии, метод ничего не будет делать - что было бы ожидаемым поведением.
Т. Сар - Восстановить Монику

Ответы:

127

Как уже сказал Ник Уильямс : понятие, которое описывает ОП, называется идемпотентом (существительное Идемпотентность ). Это действительно обычная практика, особенно в API высокого уровня.

НО: переименуйте функцию.

Вместо того чтобы startHttpServerназывать это makeSureHttpServerIsRunningили ensureHttpServerIsRunning.

Когда вызывается функция startHttpServer, читатели ожидают, что она запустит HTTP-сервер; при вызове десять раз подряд у меня будет десять запущенных серверов. Ваша функция не делает это большую часть времени. Кроме того, имя «start» говорит о том, что если я хочу, чтобы работал только один сервер, мне нужно будет отслеживать, была ли функция уже вызвана или нет.

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

gnasher729
источник
83
Этот. Лично я вместо этого использую «Обеспечить». Дело в том, что это должно стать схемой именования, понятной вашей команде.
Euphoric
3
или имейте начало выбросить исключение, если оно уже запущено как sqlconnection.open
Ewan
9
Я всегда использую имя «обеспечить» для функции «делать, если еще не».
Каз
3
Другое имя, которое я часто вижу, - это getOrCreateSomething, который создается только при первом вызове, а затем просто возвращает что-то
Fabich
5
@aroth Вы уверены, что не ожидаете, что он попытается найти любой доступный порт и вернуть его? Или просто плохо написано? ;)
jpmc26
33

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

Совершенно однозначно и ясно, что он гарантирует, что он работает (если это не так), не подразумевая перезапуск, если это так.

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

Stilez
источник
1
Большинство звонящих просто заботятся о том, чтобы сервер работал после звонка. Как это достигается, им все равно.
gnasher729
29

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

Идемпотентные методы. Методы также могут иметь свойство "идемпотентности", заключающееся в том, что (кроме ошибок с ошибками или истечением срока действия) побочные эффекты от N> 0 идентичных запросов такие же, как и для одного запроса. ( С W3.org )

Побочным эффектом сервера здесь является то, что http-сервер запускается после вызова метода. Я не вижу ничего плохого в том, что метод делает это.

Если вам нужен шаблон проектирования, я думаю, вы могли бы представить свой httpServer как одиночный, который запускается при инициализации.

Ник Уильямс
источник
5
Функция не идемпотентна, если при ее вызове один раз запускается http-сервер, а при повторном вызове - нет. Это буквально противоположность идемпотентности. Было бы идемпотентом, если бы каждый звонок запускал новый http-сервер.
Полигном
36
@Polygnome Википедия определяет идемпотентность как f (f (x)) = f (x), что в целом соответствует описанию Ника. Здесь функция ввода и вывода будет неявным состоянием сервера, поэтому состояние «сервер работает» будет фиксированной точкой для этой функции. Может быть, я неправильно понимаю статью в Википедии здесь, не могли бы вы дать ссылку на другие ссылки?
Амон
16
@Polygnome: этот ответ правильный, идемпотентность относится к функциям, для которых не имеет значения, если они вызываются один или несколько раз, результат всегда остается неизменным. Здесь результат - одна запущенная служба http, независимо от того, сколько раз вызывается функция.
Док Браун
14
Путаница возникает из-за того, что идемпотентность по своей сути является математическим понятием, применяемым к функциям. Определение хорошо и просто для тех, а также хорошо работает для чисто функциональных языков. Как только вы вводите побочные эффекты, это усложняется - вы должны рассматривать среду, в которой вы выполняете функцию, как (неявный) аргумент и вывод функции. Если это состояние не изменяется при последующих вызовах функции, оно идемпотентно, в противном случае это не так. В этом случае соответствующее состояние «работает http-сервер», поэтому функция идемпотентна.
Voo
10
@Polygnome Извините, вы не правы. Идемпотент означает, что запрос имеет одинаковый эффект, независимо от того, выполняется ли он один раз или более одного раза. Запуск N http серверов, если получено N дубликатов запроса, определенно не идемпотентен.
Kaz
7

Как тот , кто реализует этот инструмент , startHttpServerвы должны пытаться сделать это самый простой, гладкой и бесшовные для использования ...

Логика функции

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

Итак, моя точка зрения такова:

  • Реализуйте isHttpServerRunningфункцию, потому что это может понадобиться независимо в любом случае ...
  • Реализуйте, startHttpServerиспользуя его, isHttpServerRunningчтобы определить его следующее действие соответственно ...

Тем не менее, вы можете startHttpServerвернуть любое значение, которое может понадобиться пользователю этой функции, например:

  • 0 => Ошибка запуска сервера
  • 1 => Успешный запуск сервера
  • 2 => сервер уже запущен

Наименование функции

Прежде всего, какова основная цель пользователя? Чтобы запустить HTTP-сервер , верно?

По сути, нет проблем с намерением начать то, что уже началось, АКА 1*1=1. Так что, по крайней мере для меня, называть это " ensureHttpServerIsRunning", кажется, не является критически необходимым, я бы больше заботился о том, насколько длинным, естественным и запоминающимся является название функции.

Теперь, если вы хотите узнать, как подробно работает функция под капотом, для этого есть документация или источник кода, я имею в виду, как и для любой другой функции из библиотеки / framework / API / etc ...

Вы изучаете функцию один раз, а пишете ее несколько раз ...

Так или иначе, я бы придерживался того, startHttpServerчто короче, проще и понятнее, чем ensureHttpServerIsRunning.

ClemC
источник
1
Тем более, что метод должен принимать некоторые аргументы конфигурации, такие как корневой каталог, настройки безопасности, возможно, номер порта, я на 100% доволен «началом».
user949300
@ user949300: вызывающая сторона «sureHttpServerIsRunning» не заботится о конфигурации, корневом каталоге и т. д. Это дело человека, который его реализует.
gnasher729
2
@ gnasher729 Предполагается, что http-сервер - это Singleton. Синглтоны - это зло. И, возможно, здесь неуместно. Можно легко иметь несколько серверов на нескольких портах. Если на самом деле есть только один http-сервер, то весь этот метод, IMO, плохой дизайн. Лучше всего один раз запустить сервер при инициализации программы.
user949300
2

Я полагаю, что ваш коллега имел в виду, что startHttpServerделает слишком много:

  • Проверка, запущен ли сервер,
  • Запуск сервера при необходимости.

Это две не связанные части кода. Например, похожая ситуация существует, когда настольное приложение должно гарантировать, что оно еще не запущено при запуске; будет часть кода, которая обрабатывает экземпляры приложения (например, с использованием мьютекса), и код, который запускает цикл сообщений приложения.

Это означает, что у вас должен быть не один, а как минимум… два метода :

  • isHttpServerRunning: boolean
  • startHttpServer

Точка входа в приложение вызовет первый метод, а затем второй, если возвращаемое значение равно false. Теперь каждый метод делает одно и то же, и его легко понять.


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

Арсений Мурзенко
источник
21
Теперь что-то еще, вызывающее две функции, делает две вещи, плюс, выставление функций по отдельности может ввести условия гонки. Нет бесплатного обеда.
whatsisname
1
Если это слишком много для коллеги, удачи.
gnasher729
@ gnasher729: этот ответ не противоречит вашему - скорее наоборот, было бы неплохо сочетать переименование метода с разбиением его на две части. И пока мы не видим реальный код, мы не знаем, насколько сложный код.
Док Браун
10
Этот ответ, кажется, противостоит абстракции и СУХОЙ. Что, если startHttpServerв коде указано более одного места? Нужно ли копировать несколько одинаковых строк везде? Должно ли это быть сделано со всеми функциями? Довольно скоро ваша программа будет бесконечного размера.
JacquesB
3
Я думаю, что первым побочным эффектом этого подхода является то, что начало startHttpServerметода будет выглядеть примерно так if (isHttpServerRunning()){ return; }. Вы утверждаете бизнес-правило, что «запуск http-сервера недействителен, если он уже запущен», но затем возлагаете на кого-то ответственность за соблюдение этого правила. Специально и неоднократно в каждом месте, где они могут позвонить startHttpServer.
17
2

Поскольку вы не указываете язык, во JavaScript многие библиотеки имеют функцию «один раз», например Underscore . Поэтому, если вам это знакомо, назовите его «один раз» и, возможно, переименуйте ваш метод.

Сам, исходя из Java, на ум приходят термины «кэширование» или «ленивая оценка». «Идемпотент» технически правильный и хороший выбор, особенно если у вас есть более функциональный фон.

user949300
источник
Это может быть не «один раз», если сервер можно остановить.
gnasher729
@ gnasher729. Я мог представить себе редко используемый restartHttpServer()метод. Но просто остановка - какой там вариант использования? Вам нравятся спорадические сбои соединения? :-)
user949300
Нет необходимости в использовании для простой остановки HTTP-сервера, потому что он мог быть остановлен чем-то внешним по отношению к самой программе - HTTP-сервер мог отключиться и умер, администратор мог по какой-то причине вручную остановить его, и т. д.
Дейв Шерохман
Дэйв, авария "исключительная" и должна рассматриваться как таковая. Кроме того, поскольку сбой может произойти в любое время , разве не каждая строка вашего кода должна быть такой ensureRunning()? :-) Что касается того, чтобы администратор остановил его, то, чтобы этот другой код постоянно перезапускался, пока администратор пытается что-то изменить или исправить, было бы невероятно раздражающим и неправильным. Пусть админ перезапустит это, а не код.
user949300
-2

Я бы предпочел startHttpServerIfNotIsRunning.

Таким образом, условие четко упоминается уже в названии метода. Ensureили makeSureкажется мне немного расплывчатым, поскольку это не техническое выражение. Похоже, мы не знаем точно, что произойдет.

Герр дерб
источник
Я предполагаю, что вы не носитель языка? Поскольку обеспечить имеет точное определение того , что мы собираемся для. Но все же справедливо то, что документация и API не должны требовать понимания уровня носителей английского языка.
Во
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

С точки зрения архитектуры, наличие некоторого произвольного кода, управляющего веб-сервером, является кошмаром. Если только управление службами не является исключительно тем, что делает ваш код. Но я предполагаю, что вы не написали monit (или kubernetes или ...).

Андрей
источник
2
Язык - Java, и метод был разработан, чтобы запустить встроенный сервер гризли в автономном приложении Джерси. Я не могу упрекнуть в точности вашего комментария, учитывая, что я не дал вам много контекста, но вы много предполагали, делая свои заявления. Это прекрасно, если у вас есть метод, который вызывает или отключает запуск сервера.
Джон Калькот
1
Существуют миллионы бизнес-приложений, которые включают собственный HTTP-сервер для предоставления API. И всем им понадобится очень простой код, который на самом деле настраивает используемую ими библиотеку и предоставляет информацию, такую ​​как интерфейс для привязки, какой порт использовать, настройки безопасности и т. Д. Наличие startServerфункции или подобного не является чем-то необычным , Это не значит, что вы напишете мельчайшие детали низкого уровня.
Во