30分鐘掌握面向對象類的設計原則html
看過設計模式的人很多,但看過Martin的面向對象的設計原則的估計很少(詳情可參考《敏捷軟件開發:原則、模式與實踐》)。實際上這二者是相輔相成的:設計模式是具體的實踐方法,而設計原則是指導思想;設計模式讓你知道How,而設計原則讓你知道Why。編程
《敏捷軟件開發:原則、模式與實踐》原著洋洋灑灑幾十萬言,介紹面向對象類的類的設計幾個原則也有幾十頁,沒有耐心的朋友估計看不下去。不要緊,這裏我給你們一個精簡版的,讓你讀完本博就可以初步掌握這些原則,並且附送一些疑難解答,讓你更加容易理解。設計模式
這個原則看起來很簡單,就是說一個類只能承擔一個職責。ide
但這裏有一個關鍵:「職責」是如何理解的?函數
按照漢語的理解,職責其實分爲兩部分:「職」和「責」。「職」就是這個類是什麼,而「責」就是這個類要幹什麼。spa
舉個例子來講:Door是一個對象,那麼它的「職」就是門,「責」就是「開門、關門」等;而Lock的「職」就是鎖,「責」就是「上鎖、開鎖」。若是設計的時候Door同時具備鎖的職責,那麼Door就違反了SRP原則。.net
相信這是你們見得最多的原則,並且不少人都是這麼解釋的「對擴展開放、對修改封閉」,更加有人總結爲「不修改代碼增長新的功能」!設計
太神奇了,不修改代碼增長新的功能!但我難免疑惑:不修改代碼怎麼增長新的功能呢?難道代碼會像生物同樣,基因變異?orm
仔細研究事後才發現,原來是這些總結的人誤導了我,根本不是什麼「不修改代碼增長新的功能」,也不是那個省略了主語的「對擴展開放,對修改封閉」,而是「被調用者開放擴展,調用者封閉修改」htm
仍是舉門的例子:Door對象是被其它對象例如人People調用,那麼Door就是被調用者,People就是調用者。Door對象能夠擴展爲「防盜門」、「防火門」、「逃生門」等,但People在調用的時候不須要關注具體是什麼門,只須要調用這些門公共的「開門、關門」等操做便可。
這個看起來是比較難理解的原則,但我能夠給一個很容易理解的總結:「子類的輸入不能比父類多,子類的輸出不能比父類少!」。
「輸入」就是指調用類的時候要給出的條件,最多見的就是函數參數,而通常的語言均可以從語法上保證這種子類和父類相同函數的參數必須相同;還有另一種隱性的條件即「假設」或者「要求」也必須相同。
舉個簡單的例子:長方形和正方形。按照數學的定義,正方形是特殊的長方形。依照此定義看起來好像能夠將正方形定義爲長方形的子類,但實際上在面向對象設計中則是不行的,由於按照設定長方形長寬的方法不能來設定正方形的邊長,正方形要求長寬必須相等,而長方形沒有此「要求」。這就是子類的「假設」或者「假設」多於父類,違反了LSP原則。
「輸出」就是指調用類後返回的結果。即:子類的返回結果要包含父類的返回結果,能夠多但絕對不能少。
爲何設計時要知足LSP原則呢?其實很簡單,由於調用者看到的只有父類,它根本不知道究竟是哪一個子類,調用者全部的處理都是基於父類提供給調用者的信息(包括輸入信息和輸出信息)。
這個原則看起來有點嚇人,換個簡單的說法你就明白了,其實它就對應於《設計模式》開頭提到的兩個原則中的一個:「基於接口編程,而不是基於實現編程」。(另一個是什麼呢?)
這裏的「編程」包括「調用者」和「被調用者」的編程。「調用者」基於接口進行調用,「被調用者」基於接口進行具體實現。
注意:和「依賴反轉」相似的兩個比較容易混淆的概念是「依賴注入」和「控制反轉」,詳細能夠參考以下博文:http://blog.csdn.net/taijianyu/archive/2008/04/28/2338311.aspx
這個原則也很簡單,就是說一個對象不要提供多個接口,不一樣的接口應該分離到不一樣的對象上去。
雖然大師的水平不容懷疑,但這裏我仍是要懷疑一下:SRP和ISP本質上應該是一個原則,只是說法不同而已。
爲何這麼說呢?你們能夠看看SRP原則,它說的是「單一職責」。那麼職責最終體如今對象上是什麼呢?對,不就是接口麼!也就是說,若是遵循了SRP原則,接口天然就隔離了。
================================================
怎麼樣,看到這裏是否只花了30分鐘?那麼這些原則你是否基本都掌握了呢?若是還有什麼疑問,歡迎留言討論。