2013-11-28 83 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'排隊的經驗非常適合這個:) –