摘要:面向對象設計的五大原則單一職責原則接口隔離原則開放封閉原則替換原則依賴倒置原則。主要是針對繼承的設計原則,繼承與派生多態是的主要特性。
面向對象設計的五大原則:單一職責原則、接口隔離原則、開放-封閉原則、替換原則、依賴倒置原則。這些原則主要是由Robert C.Martin在《敏捷軟件開發——原則、方法、與實踐》一書中總結出來,這五大原則也是23種設計模式的基礎。
單一職責原則 Single Pesponsibility Principle, SRP
在MVC框架中,對于表單插入數據庫字段過濾與安全檢查應該是放在control層處理還是model層處理,這類問題都可以歸到單一職責的范圍。
單一職責有兩個含義:
避免相同的職責分散到不同的類中
一個類承擔太多職責
遵守SRP的好處:
減少類之間的耦合
提高類的復用性
在實際代碼開發中的應用:工廠模式、命令模式、代理模式等。
工廠模式(Factory)允許在代碼執行時實例化對象。之所以被稱為工廠模式是因為它負責“生產”對象。以數據庫為例,工廠需要的就是根據不同的參數,生成不同的實例化對象。它只負責生產對象,而不負責對象的具體內容。
定義一個適配器接口:
定義MySQL數據庫操作類:
_dbLink = @mysql_connect($config->host . (empty($config->port) ? "" : ":" . $config->port), $config->user, $config->password, true)) { if(@mysql_select_db($config->database, $this->_dbLink)){ if($config->charset){ mysql_query("SET NAMES "{$config->charset}"", $this->_dbLink); } return $this->_dbLink; } } //數據庫異常 throw new DbException(@mysql_error($this->_dbLink)); } /** * 執行數據庫查詢 * @param string $query 數據庫查詢SQL字符串 * @param mixed $handle 連接對象 * @return resource */ public function query($query, $handle) { if ($resource = @mysql_query($query, $handle)) { return $resource; } } } ?>
SQLite數據庫操作類:
_dbLink = sqlite_open($config->file, 0666, $error)) { return $this->_dbLink; } throw new DbException($error); } /** * 執行數據庫查詢 * @param string $query 數據庫查詢SQL字符串 * @param mixed $handle 連接對象 * @return resource */ public function query($query, $handle) { if ($resource = @sqlite_query($query, $handle)) { return $resource; } } } ?>
定義一個工廠類,根據傳入不同的參數生成需要的類:
調用:
$db = sqlFactory::factory("MySQL"); $db = sqlFactory::factory("SQLite");
命令模式分離“命令的請求者”和“命令的實現者”方面的職責。
模擬服務員與廚師的過程:
cook = $cook; } public function execute(){ $this->cook->meal();//把消息傳遞給廚師,讓廚師做飯 } } class DrinkCommand implements Command { private $cook; //綁定命令接受者 public function __construct(cook $cook){ $this->cook = $cook; } public function execute(){ $this->cook->drink(); } } ?>
模擬顧客與服務員的過程:
mealCommand = $mealCommand; $this->drinkCommand = $drinkCommand; } public function callMeal(){ $this->mealCommand->execute(); } public function callDrink(){ $this->drinkCommand->execute(); } } ?>
實現命令模式:
$control = new cookControl; $cook = new cook; $mealCommand = new MealCommand($cook); $drinkCommand = new DrinkCommand($cook); $control->addCommand($mealCommand, $drinkCommand); $control->callMeal(); $control->callDrink();
接口隔離原則 Interface Segregation Principle,ISP
接口隔離原則(Interface Segregation Principle,ISP)表明客戶端不應該被強迫實現一些不會使用的接口,應該把胖接口分組,用多個接口代替它,每個接口服務于一個子模塊。簡單地說,就是使用多個專門的接口比使用單個接口要好很多。
ISP主要觀點:
1.一個類對另外一個類的依賴性應當是建立在最小接口上的。
ISP可以達到不強迫客戶(接口使用者)依賴于他們不用的方法,接口的實現類應該只呈現為單一職責的角色(遵守SRP原則)。
ISP可以降低客戶之間的相互影響——當某個客戶程序要求提供新的職責(需求變化)而迫使接口發生變化時,影響到其他客戶程序的可能性會最小。
2.客戶端程序不應該依賴它不需要的接口方法(功能)。
ISP強調的是接口對客戶端的承諾越少越好,并且要做到專一。
接口污染就是為接口添加不必要的職責。“接口隔離”其實就是定制化服務設計的原則。使用接口的多重繼承實現對不同的接口的組合,從而對外提供組合功能——達到“按需提供服務”。
對于接口的污染,使用下面兩種處理方式:
利用委托分離接口。
利用多繼承分離接口。
委托模式中,有兩個對象參與處理同一個請求,接受請求的對象將請求委托給另一個對象來處理,如策略模式、代理模式等都應用到了委托的概念。
開放-封閉原則
隨著軟件系統的規模不斷增大,軟件系統的維護和修改的復雜性不斷提高,這種困境促使法國工程院士Bertrand Meyer在1998年提出了“開放-封閉”(Open-Close Principle, OCP)原則,基本思想是:
Open(Open for extension)模塊的行為必須是開放的、支持擴展的,而不是僵化的。
Closed(Closed for modification)在對模塊的功能進行擴展時,不應該影響或大規模地影響已有的程序模塊。
換句話說,也就是要求開發人員在不修改系統中現有功能代碼(源代碼或二進制代碼)的前提下,實現對應用系統的軟件功能的擴展。用一句話概括就是:一個模塊在擴展性方面應該是開放的而在更改性方面應該是封閉的。
開放-封閉能夠提高系統的可擴展性和可維護性,但這也是相對的。
以播放器為例,先定義一個抽象的接口:
interface Process { public function process(); }
然后對此接口進行擴展,實現解碼和輸出的功能:
class playerEncode implements Proess { public function process(){ echo "encode "; } } class playerOutput implements Process { public function process(){ echo "output "; } }
對于播放器的各種功能,這里是開放的。只要你遵守約定,實現了process接口,就能給播放器添加新的功能模塊。
接下來為定義播放器的線程調度管理器,播放器一旦接收到通知(可以是外部單擊行為,也可以是內部的notify行為),將回調實際的線程處理:
class playProcess { private $message = null; public function __construct(){ } public function callback(Event $event){ $this->message = $event->click(); if($this->message instanceof Process){ $this->message->process(); } } }
具體的產品出來了,在這里定義一個MP4類,這個類是相對封閉的,其中定義事件的處理邏輯:
class MP4 { public function work(){ $playProcess = new playProcess(); $playProcess->callback(new Event("encode")); $playProcess->callback(new Event("output")); } }
最后為事件分揀的處理類,此類負責對事件進行分揀,判斷用戶或內部行為,以產生正確的“線程”,供播放器內置的線程管理器調度:
class Event { private $m; public function __construct($me){ $this->m = $me; } public function click(){ switch($this->m){ case "encode": return new playerEncode(); break; case "output": return new playerOutput(); break; } } }
運行:
$mp4 = new MP4; $mp4->work(); //打印結果 encode output
如何遵守開放-封閉原則
實現開放-封閉的核心思想就是抽象編程的核心思想就是對抽象編程,而不是對具體編程,因為抽象相對穩定。讓類依賴于固定的抽象,這樣的修改就是封閉的;而通過面向對象的繼承和多態機制,可以實現對抽象體的繼承,通過覆寫其方法來改變固有的行為,實現新的擴展方法,所以對于擴展就是開放的。
1.在設計方面充分應用“抽象”和封裝的思想。
一方面就是要在軟件系統中找出各種可能的“可變因素”,并將之封裝起來;另一方面,一種可變性因素不應當散落在多個不同代碼模塊中,而應當被封裝到一個對象中。
2.在系統功能編程實現方面應用面向接口編程。
當需求發生變化時,可以提供該接口新的實現類,以求適應變化。
面向接口編程要求功能類實現接口,對象聲明為接口類型。再設計模式中,裝飾模式比較明顯地用到OCP。
替換原則
替換原則也稱里氏替換原則(Liskov Substitution Principle, LSP)的定義和主要思想如下:由于面向對象編程技術中的繼承在具體的編程中過于簡單,在許多系統的設計和編程實現中,我們并沒有認真地、理性地思考應用系統中各個類之間的繼承關系是否合適,派生類是否能正確地對其基類中的某些方法進行重寫等問題。因此經常出現濫用繼承或者錯誤地進行了繼承等現象,給系統的后期維護帶來不少麻煩。
LSP指出:子類型必須能夠替換掉它們的父類型,并出現在父類能夠出現的任何地方。
LSP主要是針對繼承的設計原則,繼承與派生(多態)是OOP的主要特性。
如何遵守LSP設計原則:
父類的方法都要在子類中實現或重寫,并且派生類只實現其抽象類中聲明的方法,而不應當給出多余的方法定義或實現。
在客戶段程序中只應該使用父類對象而不應當直接使用子類對象,這樣可以實現運行期綁定(動態綁定)。
如果A、B兩個類違反了LSP的設計,通常的做法是創建一個新的抽象類C,作為兩個具體類的超類,將A和B的共同行為移到C中,從而解決A和B行為不完全一致的問題。
依賴倒置原則 Dependence Inversion Principle, DIP
依賴倒置簡單地講就是將依賴關系倒置為依賴接口,具體概念如下:
上層模塊不應該依賴于下層模塊,它們共同依賴于一個抽象(父類不能依賴子類,它們都要依賴抽象類)。
抽象不能依賴于具體,具體應該依賴于抽象。
為什么要依賴接口?因為接口體現對問題的抽象,同時由于抽象一般是相對穩定的或者是相對變化不頻繁的,而具體是易變的。因此,依賴抽象是實現代碼擴展和運行期內綁定(多態)的基礎:只要實現了該抽象類的子類,都可以被類的使用者使用。
working(); } } class workB { private $e; public function set(employee $e){ $this->e = $e; } public function work(){ $this->e->working(); } } $worka = new workA; $worka->work(); $workb = new workB; $workb->set(new teacher()); $workb->work();
在workA中,work方法依賴于teacher實現;在workB中,work轉而依賴于抽象,這樣可以把需要的對象通過參數傳入。
在workB中,teacher實例通過setter方法傳入,從而實現了工廠模式。由于這樣的是實現是硬編碼的,為了實現代碼的進一步擴展,把這個依賴關系寫在配置文件里,指明workB需要一個teacher對象,專門由一個程序檢測配置是否正確(如所依賴的類文件是否存在)以及加載配置中所依賴的實現,這個檢測程序,就稱為IOC容器。
IOC(Inversion Of Control)是依賴倒置原則(Dependence Inversion Principle, DIP)的同義詞。依賴注入(DI)和依賴查找(DS)是IOC的兩種實現。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/28428.html
摘要:設計原則梳理,參考核心技術與最佳實踐敏捷開發原則模式與實踐,文章面向對象設計的五大原則設計模式原則單一職責原則定義特性僅有一個引起類變化的原因一個類只承擔一項職責職責變化的原因避免相同的職責分散到不同的類,功能重復問題一個類承擔的職責過多, PHP設計原則梳理,參考《PHP核心技術與最佳實踐》、《敏捷開發原則、模式與實踐》,文章PHP面向對象設計的五大原則、設計模式原則SOLID 單一...
摘要:如果看不懂的話,可以在評論區中提問,我會第一時間回答你無論何時我一直都在嗯哼該文章屬于編程中的那些經典套路設計模式匯總系列 在正式閱讀前,我先談談我們該用什么姿勢和心態學習設計模式: 如果你還沒有過多的編程經驗(泛指半年以下),我建議你把它當做小說來看,能看懂多少是多少,因為半年以下經驗的程序員用到設計模式的情況只會出現在面試上,至于實際工作中?相對來說這部分不會由你負責。 如果你已...
摘要:前言本章我們要講解的是五大原則語言實現的第篇,里氏替換原則。因此,違反了里氏替換原則。與行為有關,而不是繼承到現在,我們討論了和繼承上下文在內的里氏替換原則,指示出的面向對象。 前言 本章我們要講解的是S.O.L.I.D五大原則JavaScript語言實現的第3篇,里氏替換原則LSP(The Liskov Substitution Principle )。英文原文:http://fre...
摘要:簡單工廠模式就是遵循了這一原則,它讓不同職責的類各司其職。重點來了,簡單工廠模式的不足但是簡單工廠模式有一個不足,雖然它遵循了單一職責原則,但它違反了另一條同樣很重要的原則開放封閉原則。 該文章屬于《編程中的那些經典套路——設計模式匯總》系列,并且以下內容基于語言PHP 面向對象五大原則中有一點非常重要的原則:單一職責原則。 簡單工廠模式就是遵循了這一原則,它讓不同職責的類各司其職。 ...
摘要:,開始我們的第一篇單一職責。通過解耦可以讓每個職責工更加有彈性地變化。關于本文本文轉自大叔的深入理解系列。深入理解系列文章,包括了原創,翻譯,轉載,整理等各類型文章,原文是大叔的一個非常不錯的專題,現將其重新整理發布。 前言 Bob大叔提出并發揚了S.O.L.I.D五大原則,用來更好地進行面向對象編程,五大原則分別是: The Single Responsibility Princi...
閱讀 703·2021-11-18 10:02
閱讀 3579·2021-09-02 10:21
閱讀 1725·2021-08-27 16:16
閱讀 2058·2019-08-30 15:56
閱讀 2386·2019-08-29 16:53
閱讀 1373·2019-08-29 11:18
閱讀 2953·2019-08-26 10:33
閱讀 2642·2019-08-23 18:34