首頁  >  文章  >  Java  >  這6大設計原則Java架構師都知道嗎?一定要掌握

這6大設計原則Java架構師都知道嗎?一定要掌握

php是最好的语言
php是最好的语言原創
2018-08-01 16:23:141660瀏覽

在我們平常的開發中經常要遵順一些原則和模式,這些其實都是前人在開發中總結得到的,不管做什麼軟體開發,都將對我們有益而無害的,並且掌握了這些原則與技巧只會讓你提升效率,本文對這些原則進行了總結,也說明了其闡述的意義,給大家以參考利用價值,希望透過本文能給大家以啟發,能更好的學習Java知識。 apache php mysql

begin!

開發原則

物件導向的基本原則(solid)是五個,但是在經常被提到的除了這五個之外還有迪米特法則和合成復用原則等, 所以在常見的文章中有表示寫六大或七大原則的; 除此之外我還將給出一些其它相關書籍和互聯網上出現的原則;

S單一職責SRP

Single-Responsibility Principle, 一個類別,最好只做一件事,只有一個引起它的變化。單一職責原則可以看做是低耦合,高內聚在物件導向原則的引申,將職責定義為引起變化的原因,以提高內聚性減少引起變化的原因。

定義

一個物件應該只包含單一的職責,並且該職責被完整地封裝在一個類別中。 (Every object should have a single responsibility, and that responsibility should be entirely encapsulated  by the class.),即又定義有且僅有一個原因使類變更。點這裡查看高可用架構設計9種方案詳解。

原則分析

  • 一個類別(或大到模組,小到方法)承擔的職責越多,它被複用的可能性越小,而且如果一個類別承擔的職責太多,就等於將這些職責耦合在一起,當其中一個職責改變時,可能會影響其他職責的運作。

  • 類別的職責主要包括兩個面向:資料職責和行為職責,資料職責透過其屬性來體現,而行為職責則透過其方法來體現。

  • 單一職責原則是實現高內聚、低耦合的指導方針,在許多程式碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,需要設計人員發現類別的不同職責並將其分離,而發現類別的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。

優點

  • 降低類別的複雜性,類別的職責清晰明確。例如數據職責和行為職責清晰明確。

  • 提高類別的可讀性和維護性,

  • 變更所造成的風險減低,變更是不可或缺的,如果介面的單一職責做得好,一個介面修改只對對應的類別有影響,對其他介面無影響,這對系統的擴展性、維護性都有非常大的幫助。

注意:單一職責原則提出了一個編寫程式的標準,用「職責」或「變化原因」來衡量介面或類別設計得是否合理,但是「職責」和「變化原因」都是沒有具體標準的,一個類別到底要負責那些職責?這些職責怎麼細化?細化後是否都要有一個介面或類別?這些都需從實際的情況來考慮。因項目而異,因環境而異。

範例

SpringMVC 中Entity,DAO,Service,Controller, Util等的分離。

O開放封閉原則OCP

Open - ClosedPrinciple ,OCP, 對擴展開放,對修改關閉(設計模式的核心原則)

定義

一個軟體實體(如類別、模組和函數)應該對擴充開放,對修改關閉。意思是,在一個系統或模組中,對於擴充是開放的,對於修改是關閉的,一個 好的系統是在不修改原始碼的情況下,可以擴充你的功能。而實現開閉原則的關鍵就是抽象化。

原則分析

  • 當軟體實體因需求要變化時, 盡量透過擴展已有軟體實體,可以提供新的行為,以滿足對軟體的新的需求,而不是修改已有的程式碼,使變化中的軟體有一定的適應性和彈性。已有軟體模組,特別是最重要的抽象層模組不能再修改,這使得變化中的軟體系統有一定的穩定性和延續性。

  • 實現開閉原則的關鍵就是抽象:在」開-閉」原則中,不允許修改的是抽象的類別或介面,允許擴充的是具體的實作類,抽象類別和介面在”開-閉”原則中扮演著極其重要的角色..即要預知可能變化的需求.又預見所有可能已知的擴展..所以在這裡”抽象化”是關鍵!

  • 可變性的封閉原則:找到系統的可變因素,將它封裝起來. 這是對」開-閉」原則最好的實現. 不要把你的可變因素放在多個類別中,或散落在程式的各個角落. 你應該將可變的因素,封套起來..並且切忌不要把所用的可變因素封套在一起. 最好的解決辦法是,分塊封套你的可變因素!避免超大類別,超長類別,超長方法的出現!!給你的程式增加藝術氣息,將程式藝術化是我們的目標!

範例

設計模式中模板方法模式和觀察者模式都是開閉原則的絕佳體現。點這裡查看高可用架構設計9種方案詳解。

L里氏替換原則LSP

Liskov Substitution Principle,LSP:任何基底類別可以出現的地方,子類別也可以出現;這個想法表現為對繼承機制的約束規範,只有子類別能夠取代其基底類別時,才能夠確保系統在運作期間內辨識子類,這是保證繼承重複使用的基礎。

定義

第一種定義方式相對嚴格:如果對每一個型別為S的物件o1,都有一個類型為T的物件o2,使得以T定義的所有程式P在所有的物件o1都代換成o2時,程式P的行為沒有變化,那麼型別S是型別T的子型別。

第二種較容易理解的定義方式:所有引用基底類別(父類別)的地方必須能透明地使用其子類別的物件。即子類別能夠必須能夠替換基底類別能夠從出現的地方。子類別也能在基底類別 的基礎上新增行為。

(里氏代換原則由2008年圖靈獎得主、美國第一位電腦科學女博士、麻省理工學院教授BarbaraLiskov和卡內基.梅隆大學Jeannette Wing教授於1994年提出。其原文如下:Let q(x) be a property provableabout objects x of type T. Then q(y) should be true for objects y of type Swhere S is a subtype of T. )

#原則分析原則分析

  • 講的是基底類別和子類別的關係,只有這種關係存在時,里氏代換原則才存在。正方形是長方形是理解里氏代換原則的經典例子。

  • 里氏代換原則可以通俗表述為:在軟體中如果能夠使用基底類別對象,那麼一定能夠使用其子類別物件。把基類都替換成它的子類,程式將不會產生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類的話,那麼它不一定能夠使用基類。

  • 里氏代換原則是實現開閉原則的重要方式之一,由於使用基類對象的地方都可以使用子類對象,因此在程式中盡量使用基類類型來對物件進行定義,而運行時再確定其子類別類型,用子類別物件來取代父類別物件。

I介面隔離法則

(Interface Segregation Principle,ISL):客戶端不應該依賴那些它不需要的接口。 (這個法則與迪米特法則是相通的)

定義

客戶端不應該依賴那些它不需要的介面。

另一種定義方法:一旦一個介面太大,則需要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法即可。
注意,在該定義中的介面指的是所定義的方法。例如外面呼叫某個類別的public方法。這個方法對外就是介面。

原則分析

1)接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每個介面應該承擔一種相對獨立的角色,不多不少,不干不該幹的事,該幹的事都要幹。

  • 一個介面就只代表一個角色,每個角色都有它特定的一個接口,此時這個原則可以叫做「角色隔離原則」。

  • 介面只是提供客戶端需要的行為,即所需的方法,客戶端不需要的行為則隱藏起來,應為客戶端提供盡可能小的單獨的接口,而不要提供大的總接口。

2)使用介面隔離原則分割介面時,首先必須滿足單一職責原則,將一組相關的操作定義在一個介面中,且在滿足高內聚的前提下,介面中的方法越少越好。

3)可以在進行系統設計時採用客製化服務的方式,即為不同的客戶端提供寬窄不同的接口,只提供使用者所需的行為,而隱藏使用者不需要的行為。

D依賴倒置原則DIP

Dependency-Inversion Principle 要依賴抽象,而不要依賴具體的實現,具體而言就是高層模組不依賴底層模組,二者共同依賴抽象。抽像不依賴具體,具體依賴抽象。

定義

高層模組不應該依賴低層模組,它們都應該依賴抽象。抽像不應該依賴細節,細節應該依賴抽象。簡單的說,依賴倒置原則要求客戶端依賴抽象耦合。原則表述:

1)抽像不應依賴細節;細節應當依賴抽象;

2)要針對介面編程,不針對實作程式設計。

原則分析

1)如果說開閉原則是物件導向設計的目標,依賴倒轉原則是到達設計導向」開閉」原則的手段。如果要達到最好的」開閉」原則,就要盡量的遵守依賴倒轉原則。可以說依賴倒轉原則是對「抽象化」的最佳規範!我個人感覺,依賴倒轉原則也是里氏代換原則的補充。你了解里氏代換原則,再來理解依賴倒轉原則應該是很容易的。

2)依賴倒轉原則的常用實作方式之一是在程式碼中使用抽象類,而將具體類別放在設定檔中。

3)類別之間的耦合:零耦合關係,具體耦合關係,抽象耦合關係。依賴倒轉原則要求客戶端依賴抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵。

範例

理解這個依賴倒置,首先我們需要明白依賴在物件導向設計的概念:

依賴關係(Dependency):是一種使用關係,特定事物的改變有可能會影響到使用該事物的其他事物,在需要表示一個事物使用另一個事物時使用依賴關係。 (假設A類的變化引起了B類的變化,則說名B類依賴A類。)大多數情況下,依賴關係體現在某個類的方法使用另一個類的對像作為參數。在UML中,依賴關係以帶箭頭的虛線表示,由依賴的一方指向被依賴的一方。

範例:某系統提供一個資料轉換模組,可以將來自不同資料來源的資料轉換成多種格式,如可以轉換來自資料庫的資料(DatabaseSource)、也可以轉換來自文字檔案的資料( TextSource),轉換後的格式可以是XML檔(XMLTransformer)、也可以是XLS檔(XLSTransformer)等。

這6大設計原則Java架構師都知道嗎?一定要掌握

由於需求的變化,該系統可能需要增加新的資料來源或新的文件格式,每增加一個新的類型的資料來源或新的類型的文件格式,客戶類MainClass都需要修改原始碼,以便使用新的類,但違反了開閉原則。現使用依賴倒轉原則進行重構。點這裡查看高可用架構設計9種方案詳解。

這6大設計原則Java架構師都知道嗎?一定要掌握

  • 當然根據特定的情況,也可以將AbstractSource注入AbstractStransformer,依賴注入的方式有以下三種:

#
<ol class="hljs-ln list-paddingleft-2"><li><p class="hljs-ln-line"><span class="hljs-comment"><span class="hljs-comment">/** </span></span></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> * 依赖注入是依赖AbstractSource抽象注入的,而不是具体 </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">* DatabaseSource </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">* </span><br/></p></li><li><p class="hljs-ln-numbers"><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> */</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><br/></p></li><li><p class="hljs-ln-numbers"><span class="hljs-keyword" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">abstract</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> </span><span class="hljs-class" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractStransformer</span> </span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">{  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><span class="hljs-keyword" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">private</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> AbstractSource source;  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> </span><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-comment">/** </span></span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  * 构造注入(Constructor Injection):通过构造函数注入实例变量。 </span><br/></p></li><li><p class="hljs-ln-numbers"><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   */</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><span class="hljs-function" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">AbstractStransformer</span><span class="hljs-params">(AbstractSource source)</span></span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">{  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">     </span><span class="hljs-keyword" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">this</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">.source = source;          </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   }  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-comment">/**      </span></span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   * 设值注入(Setter Injection):通过Setter方法注入实例变量。 </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   * </span><span class="hljs-doctag" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">@param</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> source : the sourceto set        </span><br/></p></li><li><p class="hljs-ln-numbers"><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   */</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">      </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> </span><span class="hljs-function" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setSource</span><span class="hljs-params">(AbstractSource source)</span> </span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">{            </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">    </span><span class="hljs-keyword" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">this</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">.source = source;            </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> }  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-comment">/** </span></span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   * 接口注入(Interface Injection):通过接口方法注入实例变量。 </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  * </span><span class="hljs-doctag" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">@param</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> source </span><br/></p></li><li><p class="hljs-ln-numbers"><span class="hljs-comment" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">    */</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">   </span><span class="hljs-function" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">transform</span><span class="hljs-params">(AbstractSource source )</span> </span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">{    </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">      source.getSource();  </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">    System.out.println(</span><span class="hljs-string" style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">“Stransforming …”</span><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">);    </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;"> }      </span><br/></p></li><li><p class="hljs-ln-numbers"><span style="font-family: "Microsoft Yahei", "Hiragino Sans GB", Helvetica, "Helvetica Neue", 微软雅黑, Tahoma, Arial, sans-serif;">}</span></p></li></ol>

 合成/聚合復用原則

(Composite/Aggregate ReusePrinciple ,CARP):要盡量使用物件組合,而不是繼承關係達到軟體復用的目的

定義

常又叫做合成復用原則(Composite ReusePrinciple或CRP),盡量使用物件組合,而不是繼承來達到復用的目的。

就是在一個新的對象裡面使用一些已有的對象,使之成為新對象的一部分;新對象透過向這些對象的委派達到復用已有功能的目的。簡而言之,要盡量使用合成/聚合,盡量不要使用繼承。

原則分析

1)在物件導向設計中,可以透過兩種基本方法在不同的環境中重複使用現有的設計和實現,即透過組合/聚合關係或透過繼承。

繼承重複使用:實作簡單,易於擴充。破壞系統的封裝性;從基底類別繼承而來的實作是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。 (「白箱」復用)

組合/聚合復用:耦合度相對較低,選擇性地呼叫成員物件的操作;可以在運行時動態進行。 (「黑箱」多用)

2)組合/聚合可以使系統更加靈活,類別與類別之間的耦合度降低,一個類別的變化對其他類別造成的影響相對較少,因此一般首選使用組合/聚合來實現復用;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低複雜度,而濫用繼承反而會增加系統建置與維護的難度以及系統的複雜度,因此需要慎重使用繼承復用。

3)此原則和里氏代換原則氏相輔相成的,兩者都是具體實現」開-閉」原則的規範。違反這項原則,就無法實現」開-閉」原則,首先我們要明白合成和聚合的概念:

注意:聚合和組合的差異是什麼?

 

合成(組合):表示一個整體與部分的關係,指一個依托整體而存在的關係(整體與部分不可以分開);例如眼睛和嘴巴對於頭來說就是組合關係,沒有了頭就沒有眼睛和嘴,它們是不可分割的。在UML中,組合關係以實心菱形的直線表示。

 

聚合:聚合是比合成關係的一種更強的依賴關係,也表示整體與部分的關係(整體與部分可以分開);例如螺絲和汽車玩具的關係,螺絲脫離玩具仍可用在其它設備之上。在UML中,聚合關係以空心菱形的直線表示。

 迪米特法則

(Law of Demeter,LoD:系統中的類,盡量不要與其他類別互相作用,減少類之間的耦合度

定義

又叫最少知識原則(Least Knowledge Principle或簡寫為LKP)幾種形式定義:

  • 不要和「陌生人」說話。英文定義為:Don't talk to strangers.

  • 只與你的直接朋友通信。英文定義為:Talk only to your immediate friends.

  • 每一個軟體單位對其他的單位都只有最少的知識,而且局限於那些與本單位密切相關的軟體單位。

簡單地說,也就是,一個物件應該對其它物件有盡可能少的了解。一個類別應該對自己需要耦合或調用的類別知道得最少,你(被耦合或調用的類別)的內部是如何複雜都和我沒關係,那是你的事情,我就知道你提供的public方法,我就調用這麼多,其他的一概不關心。

法則分析

  • #朋友類別:

在迪米特法則中,對於一個對象,其朋友包括以下幾類: (1) 當前對象本身(this); (2)以參數形式傳入到目前物件方法中的物件; (3) 當前物件的成員物件; (4) 如果目前物件的成員物件是一個集合,那麼集合中的元素也都是朋友;(5) 當前物件所創建的對象。

任何一個對象,如果滿足上面的條件之一,就是當前對象的“朋友”,否則就是“陌生人”。

    • ##狹義法則與廣義法則:

    • 在狹義的迪米特法則中,如果兩個類別之間不必彼此直接通信,那麼這兩個類別就不應當發生直接的相互作用,如果其中的一個類別需要呼叫另一個類別的某一個方法的話,可以透過第三者轉送這個呼叫。

    • 狹義的迪米特法則:可以降低類別之間的耦合,但是會在系統中增加大量的小方法並散落在系統的各個角落,它可以使一個系統的局部設計簡化,因為每一個局部都不會和遠距離的對像有直接的關聯,但是也會造成系統的不同模組之間的通訊效率降低,使得系統的不同模組之間不容易協調。點擊這裡查看高可用架構設計9種方案詳解。
    • 廣義的迪米特法則:指對物件之間的資訊流量、流向以及資訊的影響的控制,主要是對資訊隱藏的控制。資訊的隱藏可以使各個子系統之間脫耦,從而允許它們獨立地被開發、優化、使用和修改,同時可以促進軟體的複用,由於每一個模組都不依賴其他模組而存在,因此每一個模組都可以獨立地在其他的地方使用。一個系統的規模越大,訊息的隱藏就越重要,而訊息隱藏的重要性就越明顯。

迪米特法則的主要用途:在於控制資訊的過載。

在類別的分割上,應當盡量創建鬆散耦合的類,類別之間的耦合度越低,就越有利於復用,一個處在鬆耦合中的類別一旦被修改,不會對關聯的類別造成太大波及;

#########在類別的結構設計上,每一個類別都應盡量降低其成員變數和成員函數的存取權限;############在類別的設計上,只要有可能,一個型別就應該設計成不變類別;############在對其他在類別的引用上,一個物件對其他物件的引用應降到最低。 ###############範例######外觀模式Facade(結構型)######迪米特法則與設計模式Facade模式、Mediator模式###

系統中的類,盡量不要與其他類互相作用,減少類之間的耦合度,因為在你的系統中,擴展的時候,你可能需要修改這些類,而類與類之間的關係,決定了修改的複雜度,交互作用越多,則修改難度就越大,反之,如果交互作用的越小,則修改起來的難度就越小。例如A類依賴B類,則B類依賴C類,當你在修改A類的時候,你要考慮B類是否會受到影響,而B類的影響是否又會影響到C類。如果此時C類再依賴D類的話,呵呵,我想這樣的修改有的受了。

Q&A

物件導向設計其他原則

封裝變更

少用繼承多用組合

針對介面程式設計不針對實現程式設計

為互動物件之間的鬆耦合設計而努力

類別應該對擴充開發對修改封閉(開閉OCP原則)

依賴抽象,不要依賴具體類別(依賴倒置DIP原則)

密友原則:只和朋友交談(最少知識原則,迪米特法則)

說明:一個物件應對其他物件有盡可能少的了解,將方法呼叫保持在界限內,只呼叫屬於以下範圍的方法: 該物件本身(本地方法)物件的元件被當作方法參數傳進來的物件此方法創建或實例化的任何物件

別找我(呼叫我) 我會找你(呼叫你)(好萊塢原則)

一個類別只有一個引起它變化的原因(單一職責SRP原則)

你能解釋一下里氏替換原則嗎?

嚴格定義:如果對每一個類型為S的物件o1,都有類型為T的物件o2,使得以T定義的所有程式P在所有的物件用o1取代o2時,程式P的行為沒有變化,那麼類型S是類型T的子類型。

通俗表述:所有引用基底類別(父類別)的地方必須能透明地使用其子類別的物件。也就是說子類別可以擴充父類別的功能,但不能改變父類別原有的功能。它包含以下4層意義:

子類別可以實作父類別的抽象方法,但不能覆寫父類別的非抽象方法。

子類別中可以增加自己特有的方法。

當子類別的方法重載父類別的方法時,方法的前置條件(即方法的形參)要比父類別方法的輸入參數更寬鬆。

當子類別的方法實作父類別的抽象方法時,方法的後置條件(即方法的傳回值)比父類別更嚴格。

什麼情況會違反迪米特法則?為什麼會有這個問題?

迪米特法則建議“只和朋友說話,不要陌生人說話”,以此來減少類別之間的耦合。

給我一個符合開閉原則的設計模式的例子?

開閉原則要求你的程式碼對擴充開放,對修改關閉。這個意思是說,如果你想增加一個新的功能,你可以很容易的在不改變已測試過的程式碼的前提下增加新的程式碼。有好幾個設計模式是基於開閉原則的,如策略模式,如果你需要一個新的策略,只需要實現接口,增加配置,不需要改變核心邏輯。一個正在工作的例子是Collections.sort() 方法,這就是基於策略模式,遵循開閉原則的,你不需為新的對象修改sort() 方法,你需要做的僅僅是實現你自己的Comparator 接口。

什麼時候使用享元模式(蠅量模式)?

享元模式透過共享物件來避免創建太多的物件。為了使用享元模式,你需要確保你的物件是不可變的,這樣你才能安全的共享。 JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。

end:

#如果你有什麼疑問也可以提出來的,大家一起交流。

相關文章:

JavaScript必須知道的基礎知識

Java中的24種設計模式與7大原則

相關影片:

#JavaScript高階框架設計影片教學

#

以上是這6大設計原則Java架構師都知道嗎?一定要掌握的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn