單一責任原則[Single Responsibility Principle--SRP]java
定義:There should never be more than one reason for a class to change.編程
意譯:不該該有超過一個因素引發類的改變設計模式
優勢:框架
一個類負責一個職責,複雜度下降ide
提升可讀性,可維護性工具
若是類能很好的遵循單一責任原則,將會顯著減小變動引發的聯動變化,下降了變動引起的風險學習
缺點:this
由於代碼顆粒度變細,將可能引發類膨脹
spa
理解:"定義描述很簡單,責任劃分很困難",這個應該是比較大衆的對這一原則的見解,"責任"並非一個量化的指標,"單一責任"也徹底是依靠人爲劃分的,而影響這一行爲的又有不少因素,如何在相應的項目環境中準確的定義出"單一責任"的概念是咱們須要一直努力學習的,這不是一觸而就的,須要大量的設計經驗,但起碼,咱們應當作到:面向接口編程,因此,儘管在不少條件下類不可能作到單一責任,但咱們應該儘量的把接口設計成符合單一責任原則....net
示例:考慮這樣一個場景,一個責任混雜的工具類,其中方法A依賴於外部類庫L,那麼,當把這個工具類做爲模塊提供給外部模塊使用時,那些不須要用到方法A的的模塊也將必需要依賴外部類庫L才行,使用單一責任原則,隔離出包含方法A的職責封裝成另外一個工具類,便可以解決問題.
里氏替換原則[Liskov Substitution Principle--LSP]
定義1:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
意譯1:若是對每個類型爲S的對象o1,都有類型爲T的對象o2,使得以T定義的全部程序P在全部的對象o1都替換成o2時,程序P的行爲沒有發生變化,那麼類型S是類型T的子類型.
定義2:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
意譯2:全部使用基類的地方必須能透明地使用其派生類.
優勢:
代碼共享,減小類的建立
提升代碼的重用性
提升代碼的可擴展性
缺點:
繼承是入侵的
加強了耦合
理解:從優缺點能夠看出,使用里氏替換原則的優缺點就是繼承的優缺點,由於這一原則的目標就是爲了放大繼承體系的優勢,縮小其缺點,它的使用前提就是繼承...它包含了四層含義:①子類必須徹底實現父類的方法;②子類能夠有個性[但咱們應當儘量的減小子類的個性];③子類重載繼承自父類的方法時,輸入參數(方法形參)能夠被放大;④子類重寫方法時,輸出結果(返回值)能夠被縮小.
示例:使用繼承體系時遵循里氏替換原則能幫咱們避免一些隱藏在其中的隱患,如下代碼了違背第三層含義
public class Father { public void doSth(Map map){ System.out.println("Father"); } } public class Son extends Father{ public void doSth(HashMap map){ //子類縮小了輸入參數 System.out.println("Son"); } } public static void main(String[] args) { Father temp = new Father(); //Father Son temp = new Son(); //Son HashMap map = new HashMap(); temp.doSth(map); }
能夠看到,當咱們用子類替換父類時,代碼邏輯發生了變化,咱們應當避免相似的問題發生.
依賴倒置原則[Dependence Inversion Principle--DIP]
定義:High level modules should not depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details.Details should depend upon abstractions.
意譯:高層模塊不該該依賴低層模塊,二者都應該依賴於抽象,抽象不該該依賴細節,細節應該依賴抽象.
優勢:
下降類間耦合
提升系統穩定性
提升代碼的可讀性和可維護性
利於並行開發
缺點:
項目中的類文件確定會增多(有了上述的優勢,這點代價都不肯意付出麼?)
理解:不要被名字唬住,這也許只是爲了顯示出高端(just kidding)...這一原則的核心思想就是"面向接口編程",一切皆依賴抽象,抽象是對實現的約束,同時也是與外部溝通的約束,當咱們使用抽象傳遞依賴[構造器注入,Setter注入,接口注入],搭建系統骨骼,其穩定性一定要遠遠強於直接使用具體實現的方式,而要使代碼遵循該原則,能夠經過如下幾點:①每一個類都儘可能有接口或抽象類;②變量的表面類型儘可能被聲明爲接口或抽象類③任何類都儘量避免派生自具體類;④子類儘可能不要重寫父類已經實現的方法;⑤結合里氏替換原則使用.
示例:一個只能開BWM的老司機.....
public class Driver { // 司機的主要職責就是駕駛汽車 public void drive(BMW bmw) { bmw.run(); } } public class Benz { // 汽車確定會跑 public void run() { System.out.println("奔馳汽車開始運行..."); } } public class BMW { // 寶馬車固然也能夠開動了 public void run() { System.out.println("寶馬汽車開始運行..."); } } public static void main(String[] args) { Driver zhangSan = new Driver(); BMW bmw = new BMW(); zhangSan.drive(bmw); }
本例中有C駕照的老司機居然只能開寶馬,而不能開奔馳,這顯然是不科學的....咱們對程序作以下修改以知足依賴倒置原則
public class Driver { // 司機的主要職責就是駕駛汽車 public void drive(ICar car) { car.run(); } } public class Benz implements ICar{ // 汽車確定會跑 public void run() { System.out.println("奔馳汽車開始運行..."); } } public class BMW implements ICar{ // 寶馬車固然也能夠開動了 public void run() { System.out.println("寶馬汽車開始運行..."); } } public static void main(String[] args) { Driver zhangSan = new Driver(); zhangSan.drive(new BMW()); zhangSan.drive(new Benz()); }
這纔是真正的老司機,無論你什麼車,信手拈來~~~
接口隔離原則[Interface Segregation Principle--ISP]
定義1:Clients should not be forced to depend upon interfaces that they don't use.
意譯1:客戶端不該該被強迫依賴於它所不須要的接口.
定義2:The dependency of one class to another one should depend on the smallest possible interface.
意譯2:類與類之間的依賴應該創建在儘量小的接口上.
優勢:
接口細化後,可自由"組合",易於應對變化,提升了維護性和擴展性
缺點:
會增長代碼結構複雜度
理解:這一原則和單一責任原則有些類似,但咱們應當瞭解它們的意圖是不一樣的:單一責任原則的關注點在於職責,這是從業務邏輯上的劃分[難],而接口隔離原則的關注點是接口中的方法數量要儘量的少[易];另外一方面,嚴格來講單一責任原則的原始定義是針對類,對接口和方法的原則應用是其擴展形式.因爲存在上述的類似性,咱們在設計接口時可能會碰見只能符合二者中的一個的狀況(魚與熊掌不可兼得),這時候全憑我的的設計偏好了,我更願意優先遵循單一責任原則....下面說說它具體包含的四層含義:①接口儘可能小;②高內聚;③定製服務(經過將細小的接口自由組合來爲不一樣的個體提供定製服務);④設計不能過分(很顯然,一個接口一個方法確定能知足接口隔離原則,可是同樣顯然的,你毫不會那麼設計~~~).
示例:考慮以下場景,警務系統有個內部信息查詢平臺,它提供給客戶兩個接口,查詢基本信息,查詢祕密信息,前者面向整個警務系統開放,後者只針對領導層
咱們來看第一種實現方式:
public interface IPersonInfoService { public void gainBaseInfo(); public void gainSecretInfo(); } public class PersonInfoService implements IPersonInfoService { @Override public void gainBaseInfo() { System.out.println("調用DAO層獲取基本信息接口"); } @Override public void gainSecretInfo() { System.out.println("調用DAO層獲取祕密信息接口"); } }
能夠看到,咱們提供了一個完整的接口給外部調用模塊,而後經過口頭約束來告訴外部模塊能不能查詢祕密信息
而後是第二種方式:
public interface IBaseInfoService { public void gainBaseInfo(); } public interface ISecretInfoService { public void gainSecretInfo(); } public class BaseInfoService implements IBaseInfoService { @Override public void gainBaseInfo() { System.out.println("調用DAO層獲取基本信息接口"); } } public class WholeInfoService implements IBaseInfoService, ISecretInfoService { @Override public void gainBaseInfo() { System.out.println("調用DAO層獲取基本信息接口"); } @Override public void gainSecretInfo() { System.out.println("調用DAO層獲取祕密信息接口"); } }
這裏咱們將查詢接口拆分紅兩個獨立的接口,實現類經過自由拼裝所須要實現的接口來爲外部兩個調用模塊提供單獨的定製服務
*現實場景中的業務遠遠不是像示例所描述的那般簡單的,咱們能夠想象,在具體場景中,這兩種方式的差距是巨大的.
最少知識原則[Least Knowlegde Principle--LKP]
定義:Only talk to your immediate friends.
意譯:只和你的朋友直接交談.(一個對象應該對其餘對象有最少的瞭解)
優勢:
下降耦合,提升系統靈活性
缺點:
產生的中介類會致使系統結構複雜度增長
理解:要理解這個原則,首先須要解釋出如今定義中的"朋友"一詞的含義,它指類的成員變量,輸入參數,返回值.言歸正傳,最少知識原則包含了四層含義:①只和朋友交談(類中不該該出現對"朋友"以外的訪問,JAVA,框架 API除外,畢竟通常狀況下它們是不變的);②朋友也要適度保持距離(儘可能減小public的使用);③是本身的就是本身的(若是一個方法放在本類中,既不增長類間的關係,也不會對本類產生負面影響,那就放在本類中);④減小Serializable的使用....另外,須要瞭解的是,這貨還有另外一個名字:"迪米特法則".
示例:體育老師:"體育委員,你去把女生清一下",體育委員:"哎呀,親哪一個".....
咱們來看實現:
public class Teacher { // 老師對學生髮布命令,清一下女生 public void commond(GroupLeader groupLeader) { List listGirls = new ArrayList(); // 初始化女生 for (int i = 0; i < 20; i++) { listGirls.add(new Girl()); } // 告訴體育委員開始執行清查任務 groupLeader.countGirls(listGirls); } } public class GroupLeader { // 清查女生數量 public void countGirls(List<Girl> listGirls) { System.out.println("女生數量是:" + listGirls.size()); } } public class Girl { } public static void main(String[] args) { Teacher teacher = new Teacher(); // 老師發佈命令 teacher.commond(new GroupLeader()); }
觀察示例代碼,發現Teacher類中包含了對非朋友類Girl的引用,這意味着,若是類Girl發生改變,將有可能影響到Teacher類,這樣的代碼無疑加大了變動所帶來的風險,咱們應該只和朋友交談,對代碼作出修改:
public class Teacher { // 老師對學生髮布命令,清一下女生 public void commond(GroupLeader groupLeader) { // 告訴體育委員開始執行清查任務 groupLeader.countGirls(); } } public class GroupLeader { private List<Girl> listGirls; // 傳遞全班的女生進來 public GroupLeader(List<Girl> _listGirls) { this.listGirls = _listGirls; } // 清查女生數量 public void countGirls() { System.out.println("女生數量是:" + this.listGirls.size()); } } public class Girl { } public static void main(String[] args) { // 產生一個女生羣體 List<Girl> listGirls = new ArrayList<Girl>(); // 初始化女生 for (int i = 0; i < 20; i++) { listGirls.add(new Girl()); } Teacher teacher = new Teacher(); // 老師發佈命令 teacher.commond(new GroupLeader(listGirls)); }
經過上述修改,咱們解除了Teacher類對陌生類Girl的依賴,下降了系統的耦合度,使得變動引發的風險下降.
開放關閉原則[Open Closed Principle--OCP]
定義:Software entities like classes,modules and functions should be open for extension but closed for modifications.
意譯:軟件實體好比類,模塊,方法應該對擴展開放對修改關閉.
理解:開放關閉原則是軟件開發最基礎的原則,同時也是最根本的原則,它就像一個抽象類,而上述五大原則就像是它的具體實現.咱們對開閉原則的遵循程度,直接反應了系統的穩定性,靈活性.它指導咱們用抽象來搭建系統框架,用實現來擴展細節.它是"總則".
總結
上述全部的設計原則都是一種編程建言,它們都是爲了開發出高穩定性,易於擴展維護的項目產品這一目標而建議採用的設計思想,而不是教條(這也是上述第五條採用"最少知識原則"而不使用另外一個名字"迪米特法則"的緣由),它們並無要求咱們規規矩矩的嚴格按照它們的定義來編寫代碼,事實上,在實際的開發中,咱們也很難(其實我更相信不可能,由於它們自己的定義也並不是是量化的指標)作到徹底遵循六大設計原則.當咱們仔細回憶上述法則的描述,不難發現其實不少狀況下它們都要求咱們細化代碼顆粒度以到達代碼複用,進而實現系統的靈活性,然而細化代碼是有代價的,它將使得代碼複雜度增長,因此,咱們再實現系統靈活性的同時,也在加大代碼結構複雜度,而找到其中的平衡點,纔是設計原則真正但願咱們作到的,咱們應當始終了解:設計原則,設計模式都只是手段,開發出高穩定性,易於擴展維護的項目產品纔是咱們的最終目標,若是你有信心本身開發系統具有高穩定性,又易於擴展維護,那麼你徹底能夠不懂設計原則,設計模式(若是你真的能在這樣的前提下完成這樣的系統,那麼,也僅僅是你不知道你使用了設計原則/模式,而它們實際上一定已經充斥再你的代碼中~~~,只能說,你天生是設計大師,孩子,想一想就算了),缺乏它們可能僅僅只是讓你不能在同事中裝逼~~~~而已
相關推薦
http://www.uml.org.cn/sjms/201211023.asp
http://my.oschina.net/u/1047712/blog/150173
<<HeadFirst 設計模式>>,<<設計模式之禪>>,<<設計模式 - 可複用面向對象軟件的基礎>>
備註
接口並不只僅指Interface,在設計模式中接口每每指代超類型,抽象...