术语(或“模式”?)是“如果还没有做的话就做”


54

听起来很基本,我知道,但是最近我有一位同事告诉我,这种方法startHttpServer太复杂了,难以理解,因为它只会在服务器尚未运行时启动服务器。当我回答“严重吗?我已经这样做数十年了-这是编程中的常见模式”时,我发现自己很麻烦。我经常不愿意承认他带回来的一些有据可查的证据表明,整个编程社区都在支持他的观点,而我最终感到很讨厌。

问题:如果所需的措施已经生效,则在无操作方法的概念背后是否存在文档化的设计模式?或者,如果不是模式,它是否也有名称?如果不是,是否有任何理由认为考虑以这种方式编写方法太复杂了?


6
听起来像是缓存 -而且通常确实被认为很复杂(另请参见我如何将$ {something}解释为$ {someone}?
咬到

8
您有3个不同的答案,但最好的答案是全部使用它们:重命名原始函数,将其代码拆分为两个函数(其中两个之一现在获得名称startHttpServer),是的,术语“幂等”在此处适用好。
布朗

8
您的同事提供什么样的证据?您可以提供链接吗?
罗伯特·哈维

5
我很好奇您的同事是从哪里获得报价的。至少在Stack Overflow和软件工程方面,此函数的名称最多是错误的,但不会有任何异常行为。请记住,这不是因为某处某人在博客上放了一些东西,它代表了编程社区的整个观点。甚至像马丁·福勒(Martin Fowler)这样的大人物也会时不时地说出一些很奇怪的话。我们都是人类。
T. Sar-恢复莫妮卡的时间

4
我认为,考虑“如果尚未完成,请执行一些操作”的更好方法是“将系统置于此状态”。当然,如果系统已经处于该状态,则该方法将不执行任何操作-这将是预期的行为。
T. Sar-恢复莫妮卡

Answers:


127

正如NickWilliams 已经说过的:OP描述的概念称为幂等(名词Idempotency)。这确实是常见的做法,尤其是在高级API中。

但是:重命名功能。

而不是startHttpServer称之为makeSureHttpServerIsRunningensureHttpServerIsRunning

当一个函数被调用时startHttpServer,读者希望它启动一个HTTP服务器。当连续调用十次时,我将运行十台服务器。您的函数通常不会执行此操作。另外,带有“开始”的名称表示如果我只希望运行一台服务器,则必须跟踪该函数是否已被调用。

当调用一个函数时makeSureHttpServerIsRunning,我假设它将执行必要的操作以确保HTTP服务器正在运行,很可能是通过检查HTTP服务器是否已经运行,然后以其他方式启动它。我还假定该功能可确保服务器实际正在运行(启动服务器可能需要一段时间才能完全运行)。


83
这个。我个人使用“确保”代替。关键是这应该成为您的团队理解的命名方案。
欣快感'17

3
或者已经开始抛出异常(如果已经开始)。像sqlconnection.open
Ewan

9
我总是将“确保”命名用于“如果尚未执行”功能。
卡兹(Kaz)

3
我经常看到的另一个名称是getOrCreateSomething,它仅在首次调用时创建,然后仅返回内容
Fabich

5
@aroth您确定不希望它尝试找到任何可用的端口并返回它吗?还是只是写得不好?;)
jpmc26

33

将其重命名为EnsureServerRunning

完全明确,并且很清楚,它可以确保它正在运行(如果未运行),而不暗示它会重新启动。

(替代:StartServerIfNotRunning?)


1
大多数呼叫者只关心服务器在呼叫后是否正在运行。如何实现,他们不在乎。
gnasher729

29

并不是真正的设计模式,但是我将您的方法称为幂等。该术语通常用于指代远程呼叫,但说明似乎与您的操作匹配。

等幂方法。方法还可以具有“幂等”的属性,因为(错误或过期问题除外)N> 0个相同请求的副作用与单个请求的副作用相同。(来自W3.org

服务器的副作用是,一旦调用该方法,就会启动http服务器。我发现这样做的方法没有错。

如果您需要设计模式,我想您可以将httpServer暴露为在初始化时启动的单例。


5
如果一次调用该函数启动http服务器,而第二次调用则不, 则该函数不是幂等的。那实际上就是幂等的对立面。如果每个调用都启动一个新的http服务器,那将是幂等的。
Polygnome

36
@Polygnome Wikipedia将幂等性定义为f(f(x))= f(x),通常与Nick的描述匹配。这里的功能输入和输出将是隐式服务器状态,因此“服务器正在运行”状态将是此功能的固定点。也许我对这里的维基百科文章有误解,您可以链接到其他参考文献吗?
阿蒙(Amon)

16
@Polygnome:这个答案是正确的,幂等指的是函数,无论它们被调用一次还是多次都没有关系,结果始终相同。在这里,结果是一个正在运行的http服务,而与调用该函数的次数无关。
布朗

14
混淆来自这样一个事实,即幂等性的核心是应用于功能的数学概念。对于那些人来说,定义很好,也很简单,对于纯函数式语言也很好。一旦介绍了副作用,它就会变得很复杂-您必须考虑以(隐式)自变量和函数输出的形式执行函数的环境。如果该状态没有通过进一步调用该函数进行修改,则它是幂等的,否则就不是。在这种情况下,相关状态为“ HTTP服务器正在运行”-因此该功能是幂等的。
Voo

10
@Polygnome对不起,您错了。幂等意味着该请求无论执行一次还是多次执行都具有相同的效果。如果接收到N个重复请求,则启动N个http服务器绝对不是幂等的。
卡兹(Kaz)

7

作为实施此工具的人startHttpServer您应该尝试使其最简单,平滑,无缝地使用...

函数的逻辑

从技术上讲,通过拆分startHttpServer的逻辑分为2个功能调用它们分开,所有你做什么是移动 startHttpServer幂等进代码中调用这两个函数,而不是...而且,除非你在第三个功能包两逻辑(这是什么呢startHttpServer首先),这会迫使您编写未干燥的代码,并在需要调用的所有位置成倍地复制它startHttpServer。简而言之,startHttpServer 必须调用自身的isHttpServerRunning函数。

所以我的意思是:

  • 实现isHttpServerRunning功能,因为无论如何可能需要独立地...
  • 实现startHttpServer使其用于相应isHttpServerRunning地定义其下一个动作...

不过,您可以startHttpServer返回此函数的用户可能需要的任何值,例如:

  • 0 =>服务器启动失败
  • 1 =>服务器启动成功
  • 2 =>服务器已经启动

函数的命名

首先,用户的主要目标是什么?要启动HTTP服务器,对吗?

从根本上讲,打算启动已经开始的东西(AKA)没有问题1*1=1。因此,至少对我来说,称它为“ ensureHttpServerIsRunning”似乎并非迫切需要,我会更关心函数名称的长度,自然性和难忘性。

现在,如果您想了解幕后功能的详细工作方式,请参见文档或代码源,就像是库/框架/ API /等中的任何其他功能一样...

可以在多次编写函数时学习一次函数...

因此,无论如何,我都会坚持使用startHttpServerensureHttpServerIsRunning。更短,更简单,更明确的方法。


1
尤其是由于该方法必须采用一些配置参数,例如根目录,安全设置,可能是端口号,因此我对这里的“开始”感到百分百满意。
user949300

@ user949300:“ ensureHttpServerIsRunning”的调用者不在乎配置,根目录等。这是实现它的人的事。
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
aroth

2

由于您未指定语言,因此在JavaScript中,许多库都具有“一次性”功能,例如Underscore。因此,如果您熟悉该方法,可将其称为“一次”模式,并可能重命名您的方法。

我自己,更多来自Java,想到了“缓存”或“惰性评估”。从技术上讲,“幂等”是正确的,并且是一个不错的选择,尤其是。如果您有更多的功能背景。


如果可以停止服务器,则它可能不是“一次”。
gnasher729

@ gnasher729。我可以想象一个很少使用的restartHttpServer()方法。但是只是停下来 -那里的用例是什么?您喜欢零星的连接失败吗?:-)
user949300

不必只是停止HTTP服务器的用例,因为它可能已被程序本身外部的某些东西停止了-HTTP服务器可能已隔离并已死亡,管理员可能出于某种原因手动将其停止,等等
Dave Sherohman

戴夫(Dave),崩溃是“异常的”,应这样处理。此外,由于崩溃随时可能发生,因此代码的每一行都不必ensureRunning()吗?:-)至于管理员停止它,在管理员试图修改或修复某些问题时让其他代码不断重新启动将是令人烦恼和错误的。让管理员重新启动它,而不是代码。
user949300

-2

我更喜欢startHttpServerIfNotIsRunning

这样,方法名称中已经清楚地提到了条件。EnsuremakeSure对我来说有点模糊,因为这不是技术性表达。听起来我们不知道会发生什么。


我假设您不是母语人士?因为sure 确保我们要做什么的确切定义。但是,仍然很公平的一点是,文档和API不应要求母语为英语的人也可以理解。
Voo

1
谢谢,我完全不是什么Ensure意思。我仍然不喜欢这个词来表达技术。Ensure是人类的东西。一个系统不能确保任何事情,它只会做它应该做的事情。
德尔巴先生(Herr Derb)

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

从体系结构的角度来看,拥有一些任意代码来管理Web服务器是噩梦。除非管理服务完全是您的代码所要做的。但是我猜你没有写monit(或kubernetes或...)。


2
语言是Java,该方法旨在在独立的Jersey应用程序中启动灰熊嵌入式服务器。鉴于我没有给您太多背景信息,所以我不能否认您的评论的准确性,但是您在发表声明时承担了很多责任。拥有一种可以调用码头或灰熊启动服务器的方法是完全可以的。
约翰·卡索特

1
有数百万个业务应用程序包括提供API的自托管HTTP服务器。所有这些都需要一些非常简单的代码,这些代码实际上可以设置他们正在使用的库,并提供诸如要绑定到的接口,要使用的端口,安全设置等信息。具有startServer功能或类似功能的东西并不罕见。这并不意味着您会写出一些细节。
Voo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.