2013-11-28 45 views
15

我在洗澡,想到了什么。推迟/承诺推动违反德米特法律吗?

递延/许模式是降低callback hell,通过允许开发者链通话功能,如提及here

Parse.User.logIn("user", "pass").then(function(user) { 
    return query.find(); 
}).then(function(results) { 
    return results[0].save({ key: value }); 
}).then(function(result) { 
    // the object was saved. 
}); 

从我的头顶 - 纠正我,如果我错了 - 但它似乎使用延期/承诺是打破德米特法简单的方法?

迪米特法则规定:

的对象的方法可以仅调用方法:

  • 对象本身。
  • 该方法的一个论据。
  • 该方法内创建的任何对象。
  • 对象的任何直接属性/字段。

每个单位对其他单位的知识应该是有限的:只有 单位与当前单位“密切”相关。或者:每个单位应该只有 与其朋友交谈;不要与陌生人交谈。

对此有何评论?

更新2013年12月1日:

一个总结我的问题的版本。 Promise框架旨在简化异步编码并避免“callback hell”。 Promise最有利的功能之一是,您可以使用.then()来链接呼叫事件,如上面的示例所示。

鉴于所有的代码/功能,现在使用的承诺(如本杰明Gruenbaum(下面笔者)目前正在做),会不会它打开它,使链通话功能很容易通过去.then().then().then()

编写链接调用函数的代码(.then().then().then())必须是如何打破得墨忒耳定律的教科书范例。

因此我的问题; Promise框架是否促进/开放/更容易滥用/打破Demeter法律?

+8

不能确定“淋浴声明”在这里有任何帮助... :) – benzonico

+0

JavaScript函数不需要遵守Demeter法则,这使得闭包(一种超级方便的范例)不可能。在另一种意义上,使用apply/call可能实际上会重新定义法律中的“对象”,从而在实际执行之前使JS符合。无论如何,这并不重要。 – dandavis

+0

我不明白你为什么认为这打破了德米特法则。你列出的所有方法只调用作为参数传递的函数(OO倾斜列表中第二个选项的泛化)和返回对象。在链中,那些对象有自己的方法被调用。 – kybernetikos

回答

10

我认为你误解了德米特法则的含义及其对JavaScript和承诺等框架这两种语言的适用性。

德米特法则所设想的承诺并不是一个“单位”,它与银行应用程序中诸如账户或客户之类的“类”类似。它们是异步控制流程的更高层次的元构造。很难看出这样一个元构架如何能够存在,或者是有用的,而不能够“与”它旨在控制的任意外部实体(非朋友)“交谈”。

得墨忒耳定律似乎高度关注经典的面向对象系统,其中所有东西都是类或方法。如前所述,似乎不允许任何传入函数的调用,因此也不允许大多数(如果不是全部)函数式编程。

另一种看待这个问题的方式是,如果你认为承诺违反了这个规律,那么回调肯定也会这样。毕竟,这两者基本上是同构的 - 差别本质上是句法。所以,如果你注意不违反德米特法则,你也不能使用回调 - 那么你将如何编写最基本的异步程序呢?

+0

我开始为这个答案写了一篇相当长的文章,但我想我只会对您的评论提出一个问题“那么您将如何编写最基本的异步程序?” - 您是否声明所有异步应用程序都会自动违反Demeter法律?对我来说,你可以设计你的异步应用程序,以避免“回拨地狱”,但仍尊重德米特法。你不同意? – corgrath

+0

如果您似乎担心承诺可能会违反法律,或者可能认为它们确实如此,那么回调问题确实相关,因为承诺和回调是同构的(实质上,承诺可以被视为封装的回调机制),你应该有权利也关注回调是否违法。或者相信他们这样做。相反,如果您不担心回调会违反法律,或者认为它们没有,那么根据定义,您认为承诺不会。 – 2013-12-01 15:47:34

+0

我的话题不是如果JavaScript和传统的回调违反了Demeter法则,但如果Promise框架更容易打破它。为了制作一个非常简单的(完全无关紧要的话题,但我想你会更好地理解我的问题)的例子。比方说,有一项法律规定未成年的孩子不允许喝酒,并称之为德米特法。未成年的孩子喝酒,这是不好的,因此法律。现在让我们说我们给学校食堂卖酒的可能性(我们介绍Promise框架)。 – corgrath

5

简短答案是肯定的。

我认为这已经变得更加复杂而不是必要,因为人们对这个问题感到困惑,虽然有趣的并不直接与德米特法相关。就像我们正在谈论JavaScript一样。或者我们正在处理回调的事实。那些细节不适用。

让我们退后一步并重置讨论。软件工程的首要目标是尽可能地限制耦合。换句话说,当我更改代码时,我想确保这不会迫使我在周末工作,以此来改变大量其他代码。德米特法则的存在是为了防止一种耦合类型 - feature envy。它通过对方法f可用于完成其工作的方法提供正式限制来实现。

OP @corgrath非常好,可以列举这些限制。描述违反德米特法律的简单方法是:“你不能在任何允许的4个对象上调用任何方法。”

现在终于由@corgrath提供的示例代码:

Parse.User.logIn("user", "pass").then(function(user) { 
    return query.find(); 
}).then(function(results) { 
    return results[0].save({ key: value }); 
}).then(function(result) { 
    // the object was saved. 
}); 

让我们把Parse一个数据结构而不是一个对象(见鲍勃叔叔的神话般的书Clean Code,这是第6章我第一次接触德米特法则,更多关于这个区别)。那么我们很好,Parse.User

User显然是具有方法和行为的对象。其中一种方法是logIn。这返回一个Promise。只要我们对这个物体打电话,我们违反了德米特法则

就是这样。另外,我会很快提及,在JavaScript中,functions are objects。所以德米特法也适用于传递给每个then调用的回调函数。但是,在每一个函数的方法中,都不存在这个方法,所以调用而不是违反了德米特法则。

现在有趣的是是否明确违反Demeter法律事项。软件工程是一门艺术。我们拥有各种法律,原则和实践,但宗教对他们的遵守与对他们的无知同样适得其反。尝试100%的代码覆盖率是愚蠢的;单元测试getter和setter是愚蠢的;为100%级别战斗是愚蠢的cohesion;这是愚蠢的创建100%包stability;等等

在这种情况下,我会说违反德米特法律并不重要。不以任何方式暴露内部;他们公开了执行另一个动作的抽象(在这种情况下,注册回调,但与讨论无关)。换句话说,我必须担心在周末做这些then调用?可能是实际上低。我的意思是他们可能会将方法重命名为andThenwhenYoureReadyDoThis,但我怀疑它。

这是一件大事,因为我喜欢我的周末。我不想从事不必要的事情。我想做一些有趣的事情,比如在Stack Overflow上发布作文答案。

因此,在总结,有两个问题:

  • 是否Promise代码打破迪米特法则?是。
  • 重要吗?否

将两者混淆并将各种无关信息纳入讨论中,只会令事情混淆不清。

+0

为什么在返回的承诺中调用任何内容违反了[Demeter法](https://en.wikipedia.org/wiki/Law_of_Demeter)?您可以在“*在方法*中创建/实例化的任何对象”上调用方法?否则,即使'3 * 5 + 1'也是无效的,因为我们在'15'上调用加法... ... – Bergi

3

不错的问题!尽管它没有关注坚持LoD的方面,认为与承诺模式冲突:)但是,从您的意见中可以看出,您主要关注的是链接和回调的使用。

链接在模式的实现中很常见,但不会增加其语义;其实它只不过是语法糖,使得下面的代码是同义词:

someAsyncPromise.then(/*do some stuff first*/).then(/*do other stuff*/); 

someAsyncPromise.then(/*do some stuff first*/); 
someAsyncPromise.then(/*do other stuff*/); 

以上,then方法返回到原来的承诺对象someAsyncPromise一个新的/不同的承诺对象的引用。这可能违反了其他一些OO原则,但不毁灭之王,它是犹太的对象调用它自己的方法(D'哦:)也许它更容易在普通的jQuery选择认可链接:

$('#element').hide().text('oh hi').css('color', 'fuchsia'); 
//synonymous to 
$('#element').hide(); 
$('#element').text('oh hi'); 
$('#element').css('color', 'fuchsia'); 

(好吧,不真的的代名词,因为$()选择将重新查询在第二个例子中的元素;然而现在它如果我们缓存jQuery对象你得到点)

让我们专注于回调!。 JavaScript的美妙之处在于,功能是一等公民,您可以将它们传递给它们。你的例子中的noop做什么?

function(result) { 
    // the object was saved. 
} 

它只是坐在那里,像一个等待孵化的鸡蛋。您正在使用函数表达式来创建一个匿名函数,然后将其作为参数.then()进行传递,该函数会将其推入一堆要执行的承诺中。

因此,在某种程度上,承诺模式的一个解释是,看它作为秉承到毁灭之王一个教科书式的范例!

...

让我把话说完说目前的维基百科条目,恕我直言,严重误导:

对于使用点作为现场标识许多现代面向对象的语言,法律可以简单地为“只使用被称一个点“。也就是说,代码a.b.Method()违反了法律,a.Method()没有。

Lolwut?这太重视语法和外观,而不是基础结构和体系结构。有a very comprehensive explanation涵盖了两者之间的区别(以及维基百科条目中的一些其他泄漏),以及the law's non-wikified version,您可能想要仔细阅读它们。

+4

'.then()'不会返回相同的承诺,现在甚至不会在搞砸jQuery承诺(因为1.8: ))。这样的“承诺”将完全没有价值,并且不会给你带来真正的好处。 – Esailija

+0

@Esailija:好点; tbh我并没有专注于jQuery的延期--jQuery只是作为链接的一个例子。我最近使用'Q'和'webdriver'排队的经验非常适合这个:) –