阿里P7架構師告訴你Java架構師必須知道的 6 大設計原則

在軟件開發中,前人對軟件系統的設計和開發總結了一些原則和模式, 無論用什麼語言作開發,都將對咱們系統設計和開發提供指導意義。本文主要將總結這些常見的原則,和具體闡述意義。數據庫

開發原則

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

S單一職責SRP

Single-Responsibility Principle, 一個類,最好只作一件事,只有一個引發它的變化。單一職責原則能夠看作是低耦合,高內聚在面向對象原則的引伸,將職責定義爲引發變化的緣由,以提升內聚性減小引發變化的緣由。設計模式

定義

一個對象應該只包含單一的職責,而且該職責被完整地封裝在一個類中。(Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.),即又定義有且僅有一個緣由使類變動。點擊這裏查看高可用架構設計9種方案詳解。緩存

原則分析

  • 一個類(或者大到模塊,小到方法)承擔的職責越多,它被複用的可能性越小,並且若是一個類承擔的職責過多,就至關於將這些職責耦合在一塊兒,當其中一個職責變化時,可能會影響其餘職責的運做。
  • 類的職責主要包括兩個方面:數據職責和行爲職責,數據職責經過其屬性來體現,而行爲職責經過其方法來體現。
  • 單一職責原則是實現高內聚、低耦合的指導方針,在不少代碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,須要設計人員發現類的不一樣職責並將其分離,而發現類的多重職責須要設計人員具備較強的分析設計能力和相關重構經驗。

優勢

  • 下降類的複雜性,類的職責清晰明確。好比數據職責和行爲職責清晰明確。
  • 提升類的可讀性和維護性,
  • 變動引發的風險減低,變動是必不可少的,若是接口的單一職責作得好,一個接口修改只對相應的類有影響,對其餘接口無影響,這對系統的擴展性、維護性都有很是大的幫助。
注意:單一職責原則提出了一個編寫程序的標準,用「職責」或「變化緣由」來衡量接口或類設計得是否合理,可是「職責」和「變化緣由」都是沒有具體標準的,一個類到底要負責那些職責?這些職責怎麼細化?細化後是否都要有一個接口或類?這些都需從實際的狀況考慮。因項目而異,因環境而異。

O開放封閉原則OCP

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

定義

一個軟件實體(如類、模塊和函數)應該對擴展開放,對修改關閉。意思是,在一個系統或者模塊中,對於擴展是開放的,對於修改是關閉的,一個 好的系統是在不修改源代碼的狀況下,能夠擴展你的功能。而實現開閉原則的關鍵就是抽象化。架構

原則分析

  • 當軟件實體因需求要變化時, 儘可能經過擴展已有軟件實體,能夠提供新的行爲,以知足對軟件的新的需求,而不是修改已有的代碼,使變化中的軟件有必定的適應性和靈活性 。已有軟件模塊,特別是最重要的抽象層模塊不能再修改,這使變化中的軟件系統有必定的穩定性和延續性。
  • 實現開閉原則的關鍵就是抽象化 :在"開-閉"原則中,不容許修改的是抽象的類或者接口,容許擴展的是具體的實現類,抽象類和接口在"開-閉"原則中扮演着極其重要的角色..即要預知可能變化的需求.又預見全部可能已知的擴展..因此在這裏"抽象化"是關鍵!
  • 可變性的封閉原則:找到系統的可變因素,將它封裝起來. 這是對"開-閉"原則最好的實現. 不要把你的可變因素放在多個類中,或者散落在程序的各個角落. 你應該將可變的因素,封套起來..而且切忌不要把所用的可變因素封套在一塊兒. 最好的解決辦法是,分塊封套你的可變因素!避免超大類,超長類,超長方法的出現!!給你的程序增長藝術氣息,將程序藝術化是咱們的目標!

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. 接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每個接口應該承擔一種相對獨立的角色,很少很多,不幹不應乾的事,該乾的事都要幹。
  • 一個接口就只表明一個角色,每一個角色都有它特定的一個接口,此時這個原則能夠叫作「角色隔離原則」。
  • 接口僅僅提供客戶端須要的行爲,即所需的方法,客戶端不須要的行爲則隱藏起來,應當爲客戶端提供儘量小的單獨的接口,而不要提供大的總接口。
  1. 使用接口隔離原則拆分接口時,首先必須知足單一職責原則,將一組相關的操做定義在一個接口中,且在知足高內聚的前提下,接口中的方法越少越好。
  2. 能夠在進行系統設計時採用定製服務的方式,即爲不一樣的客戶端提供寬窄不一樣的接口,只提供用戶須要的行爲,而隱藏用戶不須要的行爲。

D依賴倒置原則DIP

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

定義

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

  • 抽象不該當依賴於細節;細節應當依賴於抽象;
  • 要針對接口編程,不針對實現編程。

原則分析

  1. 若是說開閉原則是面向對象設計的目標,依賴倒轉原則是到達面向設計"開閉"原則的手段。若是要達到最好的"開閉"原則,就要儘可能的遵照依賴倒轉原則。能夠說依賴倒轉原則是對「抽象化」的最好規範!我我的感受,依賴倒轉原則也是里氏代換原則的補充。你理解了里氏代換原則,再來理解依賴倒轉原則應該是很容易的。
  2. 依賴倒轉原則的經常使用實現方式之一是在代碼中使用抽象類,而將具體類放在配置文件中。
  3. 類之間的耦合:零耦合關係,具體耦合關係,抽象耦合關係。依賴倒轉原則要求客戶端依賴於抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵。

合成/聚合複用原則

(Composite/Aggregate ReusePrinciple ,CARP):要儘可能使用對象組合,而不是繼承關係達到軟件複用的目的

定義

常常又叫作合成複用原則(Composite ReusePrinciple或CRP),儘可能使用對象組合,而不是繼承來達到複用的目的。

就是在一個新的對象裏面使用一些已有的對象,使之成爲新對象的一部分;新對象經過向這些對象的委派達到複用已有功能的目的。簡而言之,要儘可能使用合成/聚合,儘可能不要使用繼承。

原則分析

  1. 在面向對象設計中,能夠經過兩種基本方法在不一樣的環境中複用已有的設計和實現,即經過組合/聚合關係或經過繼承。

繼承複用:實現簡單,易於擴展。破壞系統的封裝性;從基類繼承而來的實現是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。(「白箱」複用)
組合/聚合複用:耦合度相對較低,選擇性地調用成員對象的操做;能夠在運行時動態進行。(「黑箱」複用)

  1. 組合/聚合可使系統更加靈活,類與類之間的耦合度下降,一個類的變化對其餘類形成的影響相對較少,所以通常首選使用組合/聚合來實現複用;其次才考慮繼承,在使用繼承時,須要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,下降複雜度,而濫用繼承反而會增長系統構建和維護的難度以及系統的複雜度,所以須要慎重使用繼承複用。
  2. 此原則和里氏代換原則氏相輔相成的,二者都是具體實現"開-閉"原則的規範。違反這一原則,就沒法實現"開-閉"原則,首先咱們要明白合成和聚合的概念:

注意:聚合和組合的區別是什麼?

合成(組合):表示一個總體與部分的關係,指一個依託總體而存在的關係(總體與部分不能夠分開);好比眼睛和嘴對於頭來講就是組合關係,沒有了頭就沒有眼睛和嘴,它們是不可分割的。在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種方案詳解。

  • 廣義的迪米特法則:

指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可使各個子系統之間脫耦,從而容許它們獨立地被開發、優化、使用和修改,同時能夠促進軟件的複用,因爲每個模塊都不依賴於其餘模塊而存在,所以每個模塊均可以獨立地在其餘的地方使用。一個系統的規模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。

  • 迪米特法則的主要用途:
  1. 在於控制信息的過載。
  2. 在類的劃分上,應當儘可能建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類形成太大波及;
  3. 在類的結構設計上,每個類都應當儘可能下降其成員變量和成員函數的訪問權限;
  4. 在類的設計上,只要有可能,一個類型應當設計成不變類;
  5. 在對其餘類的引用上,一個對象對其餘對象的引用應當降到最低。

面向對象設計其餘原則

封裝變化

少用繼承 多用組合

針對接口編程 不針對實現編程

爲交互對象之間的鬆耦合設計而努力

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

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

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

說明:一個對象應當對其餘對象有儘量少的瞭解,將方法調用保持在界限內,只調用屬於如下範圍的方法: 該對象自己(本地方法)對象的組件 被看成方法參數傳進來的對象 此方法建立或實例化的任何對象

別找我(調用我) 我會找你(調用你)(好萊塢原則)

一個類只有一個引發它變化的緣由(單一職責SRP原則)

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

嚴格定義:若是對每個類型爲S的對象o1,都有類型爲T的對象o2,使得以T定義的全部程序P在全部的對象用o1替換o2時,程序P的行爲沒有變化,那麼類型S是類型T的子類型。

通俗表述:全部引用基類(父類)的地方必須能透明地使用其子類的對象。也就是說子類能夠擴展父類的功能,但不能改變父類原有的功能。它包含如下4層含義:

子類能夠實現父類的抽象方法,但不能覆蓋父類的非抽象方法。

子類中能夠增長本身特有的方法。

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

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

什麼狀況下會違反迪米特法則?爲何會有這個問題?

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

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

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

何時使用享元模式(蠅量模式)?

享元模式經過共享對象來避免建立太多的對象。爲了使用享元模式,你須要確保你的對象是不可變的,這樣你才能安全的共享。JDK 中 String 池、Integer 池以及 Long 池都是很好的使用了享元模式的例子。

歡迎你們加入粉絲交流羣: 963944895,免費分享Spring框架、Mybatis框架SpringBoot框架、SpringMVC框架、SpringCloud微服務、Dubbo框架、Redis緩存、RabbitMq消息、JVM調優、Tomcat容器、MySQL數據庫教學視頻及架構學習思惟導圖

寫在最後:

既然看到這裏了,以爲筆者寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

相關文章
相關標籤/搜索