設計模式(六)------設計模式六大原則(5):迪米特法則

轉載自:http://www.jianshu.com/p/14589fb6978e (作者簡書:涅槃1992)

揭秘迪米特法則

迪米特法則(Law of demeter,縮寫是LOD)要求:一個對象應該對其他對象保持最少了解, 通縮的講就是一個類對自己依賴的類知道的越少越好,也就是對于被依賴的類,向外公開的方法應該盡可能的少。

迪米特法則還有一種解釋:Only talk to your immediate friends,即只與直接朋友通信.首先來解釋編程中的朋友:兩個對象之間的耦合關系稱之為朋友,通常有依賴,關聯,聚合和組成等.而直接朋友則通常表現為關聯,聚合和組成關系,即兩個對象之間聯系更為緊密,通常以成員變量,方法的參數和返回值的形式出現.

那么為什么說是要與直接朋友通信呢?觀察直接朋友出現的地方,我們發現在直接朋友出現的地方,大部分情況下可以接口或者父類來代替,可以增加靈活性. (需要注意,在考慮這個問題的時候,我們只考慮新增的類,而忽視java為我們提供的基礎類.)

實例演示

不難發現,迪米特法則強調了一下兩點:

  • 第一要義:從被依賴者的角度來說:只暴露應該暴露的方法或者屬性,即在編寫相關的類的時候確定方法/屬性的權限
  • 第二要義:從依賴者的角度來說,只依賴應該依賴的對象

先來解釋第一點,我們使用計算機來說明,以關閉計算機為例:

當我們按下計算機的關機按鈕的時候,計算機會執行一些列的動作會被執行:比如保存當前未完成的任務,然后是關閉相關的服務,接著是關閉顯示器,最后是關閉電源,這一系列的操作以此完成后,計算機才會正式被關閉。

現在,我們來用簡單的代碼表示這個過程,在不考慮迪米特法則情況下,我們可能寫出以下代碼

//計算機類
public class Computer{public void saveCurrentTask(){//do something}public void closeService(){//do something}public void closeScreen(){//do something}public void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//人
public class Person{private Computer c;...public void clickCloseButton(){//現在你要開始關閉計算機了,正常來說你只需要調用close()方法即可,//但是你發現Computer所有的方法都是公開的,該怎么關閉呢?于是你寫下了以下關閉的流程:        c.saveCurrentTask();c.closePower();c.close();//亦或是以下的操作        c.closePower();//還可能是以下的操作c.close();c.closePower();}}

發現上面的代碼中的問題了沒?
我們觀察clickCloseButton()方法,我們發現這個方法無法編寫:c是一個完全暴露的對象,其方法是完全公開的,那么對于Person來說,當他想要執行關閉的時候,卻發現不知道該怎么操作:該調用什么方法?靠運氣猜么?如果Person的對象是個不按常理出牌的,那這個Computer的對象豈不是要被搞壞么?

迪米特法則第一要義

現在我們來看看迪米特法則的第一點:從被依賴者的角度,只應該暴露應該暴露的方法。那么這里的c對象應該哪些方法應該是被暴露的呢?很顯然,對于Person來說,只需要關注計算機的關閉操作,而不關心計算機會如何處理這個關閉操作,因此只需要暴露close()方法即可。
那么上述的代碼應該被修改為:

//計算機類
public class Computer{private void saveCurrentTask(){//do something}private void closeService(){//do something}private void closeScreen(){//do something}private void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//人
public class Person{private Computer c;...public  void clickCloseButton(){c.close();}}

看一下它的類圖:


接下來,我們繼續來看迪米特法則的第二層含義:從依賴者的角度來說,只依賴應該依賴的對象。 這句話令人有點困惑,什么叫做應該依賴的對象呢?我們還是用上面“關閉計算機”的例子來說明: 準確的說,計算機包括操作系統和相關硬件,我們可以將其劃分為System對象和Container對象。當我們關閉計算機的時候,本質上是向操作系統發出了關機指令,而實則我們只是按了一下關機按鈕,也就是我們并沒有依賴System的對象,而是依賴了Container。這里Container就是我們上面所說的直接朋友---只和直接朋友通信.

我們就這點,繼續深入討論一下: only talk to your immedate friends 這句話只說明了要和直接朋友通信,但是我覺得這還不完整,我更愿意將其補充為: make sure your friends,only talk to your immedate friends,don't speak to strangers. 大意是:確定你真正的朋友,并只和他們通信,并且不要和陌生人講話.這樣做有個很大的好處就是,能夠簡化對象與對象之間的通信,進而減輕依賴,提供更高的靈活性,當然也可以提供一定的安全性.

現在來想想現實世界中的這么一種情況:你是一個令人矚目的公眾人物,周圍的每個人都知道你的名字,當你獨自走在大街上的時候會是怎么樣的一種場景?每個人都想要和你聊天!,和你交換信息!!接著,你發現自己已經寸步難行了.如果這時候你有一個經紀人,來幫你應對周圍的人,而你就只和這個經紀人通信,這樣就大大減輕了你的壓力,不是么?此時,這個經濟人就相當于你的直接朋友.


迪米特法則第二要義

現在,我們再回顧"關機計算機"這個操作,前面的代碼只是遵從了"暴漏應該暴漏的方法"這一點,現在我們結合第二點來進行改進:System和Container相比,System并非Person的直接朋友,而Container才是(Person直接打交道的是Container).因此我們需要將原有的Computer拆分成System和Cotainer,然后使Person只與Container通信,因此代碼修改為:

//操作系統
public class System{private void saveCurrentTask(){//do something}private void closeService(){//do something}private void closeScreen(){//do something}private void closePower(){//do something}public void close(){saveCurrentTask();closeService();closeScreen();closePower();}
}//硬件設備容器
public class Container{private System mSystem;public void sendCloseCommand(){mSystem.close();}
}//人
ublic class Person{private Container c;....public void clickCloseButton(){c.sendCloseCommand();}}

來看一下它的類圖:



對比這兩種方案,明顯這種方案二的解耦程度更高,靈活大大增強.不難發現,這應用了我們前面提到的依賴倒置,即面向接口編程.

除此之外,我們發現隨著不斷的改進,類的數量也在不斷的增加,從2個增加到5個,這意味著為了解耦和提高靈活性通常要編寫的類的數量會翻倍.因此,你需要在這做一個權衡,切莫刻意為了追求設計,而導致整個系統非常的冗余,最終可能得不償失.


總結

有人會覺得Container像是一個中介(代理).沒錯,我們確實可以稱其為中介,但這并不能否認他是我們的直接朋友:在很多情況下,中介可以說是我們的一種代表,因此將其定義為直接朋友是沒有任何問題的.比如,當你想要租房的時候,你可以找房屋中介,對方會按照你的標準為你尋找合適的住房.但是問題來了:那么做一件事情需要多少中介呢?總不能是我委托一個中介A幫我找房子,但中介A又委托了中介B,中介B又委托了中介C....等等,如果真的是這樣,那還不如我自己去找房子效率更高.在實際開發中,委托的層次要控制在6層以下,多余6層以上的會使得系統過分的冗余和并切會委托層次過多而導致開發人員無法正確的理解流程,產生風險的可能會大大提高.

到目前,我們已經徹底的了解了迪米特法則.



轉載于:https://www.cnblogs.com/chz-blogs/p/9380982.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/251361.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/251361.shtml
英文地址,請注明出處:http://en.pswp.cn/news/251361.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

http://www.cda.cn/view/25735.html

通過實例淺析Python對比C語言的編程思想差異 我一直使用 Python,用它處理各種數據科學項目。 Python 以易用聞名。有編碼經驗者學習數天就能上手(或有效使用它)。 聽起來很不錯,不過,如果你既用 Python,同時…

前端知識點梳理(一)

一、HTML 1. meta標簽 記住2個屬性&#xff1a;name和http-equiv name&#xff1a;描述網頁 <meta name"參數" content"具體的描述">http-equiv&#xff1a;文件頭 HTML中的meta標簽及其使用方法 二、CSS 1. css實現水平居中的幾種方式 css實…

Babel 7 基礎入門學習(詳細版)

可以在我的GitHub上下載示例代碼。 前言 之前一直想要系統的學習一下Babel的使用規則&#xff0c;看過阮一峰老師的《Babel基礎入門》&#xff0c;無奈此教程是2016年出的&#xff0c;而Babel 7都已經出來啦&#xff0c;于是&#xff0c;在搜集了各種資料后&#xff0c;關于…

JS的DOM操作

1.DOM節點 &#xff08;1&#xff09;node.offsetParent最近的有定位屬性的祖先節點 如果祖先節點都沒有定位&#xff0c;那么默認為body &#xff08;2&#xff09;node.offsetLeft/node.offsetTop 距離最近的有定位屬性的祖先節點的距離 node.offsetLeft左外邊框到定位父級的…

Kubernetes學習之路(四)之Node節點二進制部署

K8S Node節點部署 1、部署kubelet &#xff08;1&#xff09;二進制包準備 [rootlinux-node1 ~]# cd /usr/local/src/kubernetes/server/bin/ [rootlinux-node1 bin]# cp kubelet kube-proxy /opt/kubernetes/bin/ [rootlinux-node1 bin]# scp kubelet kube-proxy 192.168.56.1…

前端知識點梳理(二)

1.內核 瀏覽器內核&#xff08;Rendering Engine&#xff09;最初分為&#xff1a;渲染引擎&#xff08;layout engineer&#xff09;或&#xff08;Rendering Engine&#xff09;和js引擎&#xff1b;后來 JS 引擎越來越獨立&#xff0c;內核就傾向于單指渲染引擎。瀏覽器she…

微信小程序模仿開眼視頻app(三)——信息卡片瀑布流和分類

《微信小程序模仿開眼視頻app&#xff08;一&#xff09;——視頻首頁、視頻詳情、分類》 《微信小程序模仿開眼視頻app&#xff08;二&#xff09;——搜索功能》 可到我的github賬號上去copy文件 瀑布流部分 文件代碼提示的挺詳細的&#xff0c;這里主要點一下 社區與分類…

PHP后臺代碼解決跨域問題

在前端里面&#xff0c;解決跨域的時候總顯得那么的惡心&#xff0c;什么jsonp啊&#xff0c;ajax啊&#xff0c;CORS啊什么的&#xff0c;總覺得是在鉆空子進行跨域&#xff0c;其實在PHP文件里面只需要加一段代碼就可以跨域了&#xff0c;前端你該怎么寫還是怎么寫&#xff0…

javascript --- typeof方法和instanceof方法

ES5中: 原始類型包括:Number、String、Boolean、Null、Undefined 原始封裝類型包括:Number、String、Boolean 引用類型包括:Array、Function、RegExp、Error、Date、Error 變量對象 原始類型的實例成為原始值,它直接保存在變量對象中. 引用類型的實例成為引用值,它作為一個指針…

python 基本數據類型常用方法總結

【引言】 python中基本數據類型的有很多常用方法&#xff0c;熟悉這些方法有助于不僅提升了編碼效率&#xff0c;而且能寫出高質量代碼&#xff0c;本文做總結 int .bit_length:返回二進制長度 str 切片索引超出不會報錯 切片上下限寫反不報錯&#xff0c;沒有結果 切片倒取&am…

網易試題——關于箭頭函數與this和arguments的關系

昨天做試題的時候遇到了這個題目 var a 1;function fn1() {console.log(this.a)}const fn2 () > {console.log(this.a)}const obj {a: 10,fn1: fn1,fn2: fn2}fn1()fn2()obj.fn1()obj.fn2() 哦這該死的網易&#xff0c;怎么出這么簡單的題目&#xff0c;答案是&#xff1…

《JavaScript 高級程序設計》筆記 第1~5章

第1章 js是專為網頁交互而設計的腳本語言&#xff0c;由3部分組成&#xff1a; ECMAScript&#xff0c;提供核心語言功能DOM文檔對象模型&#xff0c;提供訪問和操作網頁內容的方法和接口BOM瀏覽器對象模型&#xff0c;提供與瀏覽器交互的方法和接口 js是一種腳本語言、解釋…

【筆記】跨域重定向中使用Ajax(XHR請求)導致跨域失敗

背景&#xff1a; 1、前端Web中有兩個域名&#xff0c;a.com和b.com&#xff0c;其中a.com是訪問主站&#xff08;頁面&#xff09;&#xff0c;b.com是數據提交接口的服務器&#xff08;XHR請求&#xff09; 2、a.com中用XHR調用b.com/cerate【沒有指定協議】&#xff0c;保存…

javascript --- js中prototype、__proto__、[[Propto]]、constructor的關系

首先看下面一行代碼: function Person(name){this.name name; } var person1 new Person; console.log(person1.__proto__ Person.prototype); console.log(person1.constructor Person);控制臺打印如下: 可以看見,當使用構造函數(Person)構造一個實例(person1)時, 在后…

前端知識點整理收集(不定時更新~)

知識點都是搜集各種大佬們的&#xff0c;如有冒犯&#xff0c;請告知&#xff01; 目錄 原型鏈 New關鍵字的執行過程 ES6——class constructor方法 類的實例對象 不存在變量提升 super 關鍵字 ES6——...&#xff08;展開/收集&#xff09;運算符 面向對象的理解 關…

數據庫四大特性與隔離級別

數據庫四大特性ACID Atomicity (原子性) :事務&#xff08;transaction&#xff09;是由指邏輯上對數據的的一組操作&#xff0c;這組操作要么一次全部成功&#xff0c;如果這組操作全部失敗&#xff0c;是不可分割的一個工作單位。 Consistency(一致性) :在事務開始以前&#…

重學《JavaScript 高級程序設計》筆記 第6章對象

第6章 面向對象的程序設計 ECMAScript中沒有類的概念&#xff1b; 1.創建對象-歷史 1.1 創建實例&#xff0c;添加方法和屬性 → 對象字面量 缺點&#xff1a; 使用同一接口創建很多對象&#xff0c;產生大量重復代碼 var person new Object() person.name "Y" pe…

Java-reflect(反射)初步理解_1

27.01_反射(類的加載概述和加載時機) A:類的加載概述 當程序要使用某個類時&#xff0c;如果該類還未被加載到內存中&#xff0c;則系統會通過加載&#xff0c;連接&#xff0c;初始化三步來實現對這個類進行初始化。加載 就是指將class文件讀入內存&#xff0c;并為之創建一個…

javascrip --- 構造函數的繼承

兩點需要注意的. 第一是在構造函數聲明時,會同時創建一個該構造函數的原型對象,而該原型對象是繼承自Object的原型對象 // 聲明一個構造函數Rectengle function Rectangle(length, width) {this.length length;this.width width; }// 即:看見function 后面函數名是大寫,一般…

Ruby實例方法和類方法的簡寫

創建: 2017/12/12 類方法 Sample.func實例方法 Sample#func轉載于:https://www.cnblogs.com/lancgg/p/8281677.html