面向對象設計的五大原則:單一職責原則、接口隔離原則、開放-封閉原則、替換原則、依賴倒置原則。這些原則主要是由Robert C.Martin在《敏捷軟件開發——原則、方法、與實踐》一書中總結出來,這五大原則也是23種設計模式的基礎。php
單一職責原則 Single Pesponsibility Principle, SRPmysql
在MVC框架中,對於表單插入數據庫字段過濾與安全檢查應該是放在control層處理仍是model層處理,這類問題均可以歸到單一職責的範圍。sql
單一職責有兩個含義:數據庫
遵照SRP的好處:編程
在實際代碼開發中的應用:工廠模式、命令模式、代理模式等。
工廠模式(Factory)容許在代碼執行時實例化對象。之因此被稱爲工廠模式是由於它負責「生產」對象。以數據庫爲例,工廠須要的就是根據不一樣的參數,生成不一樣的實例化對象。它只負責生產對象,而不負責對象的具體內容。設計模式
定義一個適配器接口:安全
<?php interface DbAdapter { /** * 數據庫鏈接 * @param $config 數據庫配置 * @return resource */ public function connect($config); /** * 執行數據庫查詢 * @param string $query 數據庫查詢SQL字符串 * @param mixed $handle 鏈接對象 * @return resource */ public function query($query, $handle); } ?>
定義MySQL數據庫操做類:框架
<?php class DbAdapterMysql implements DbAdapter { private $_dbLink; //數據庫鏈接字符串表示 /** * 數據庫鏈接函數 * @param $config 數據庫配置 * @throws DbException * @return resource */ public function connect($config) { if($this->_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數據庫操做類:函數
<?php class DbAdapterSqlite implements DbAdapter { private $_dbLink; /** * 數據庫鏈接函數 * @param $config 數據庫配置 * @throws DbException * @return resource */ public function connect($config) { if ($this->_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; } } } ?>
定義一個工廠類,根據傳入不一樣的參數生成須要的類:this
<?php class sqlFactory { public static function factory($type) { if (include_once 'Drivers/' . $type . '.php') { $classname = 'DbAdapter' . $type; return new $classname; } else { throw new Exception('Driver not found'); } } } ?>
調用:
$db = sqlFactory::factory('MySQL'); $db = sqlFactory::factory('SQLite');
命令模式分離「命令的請求者」和「命令的實現者」方面的職責。
<?php /** * 廚師類,命令接受者與執行者 */ class cook { public function meal(){ echo '番茄炒雞蛋',PHP_EOL; } public function drink(){ echo '紫菜蛋花湯',PHP_EOL; } public function ok(){ echo '完畢',PHP_EOL; } } /** * 命令接口 */ interface Command{ public function execute(); } ?>
模擬服務員與廚師的過程:
<?php class MealCommand implements Command { private $cook; //綁定命令接受者 public function __construct(cook $cook){ $this->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(); } } ?>
模擬顧客與服務員的過程:
<?php class cookControl { private $mealCommand; private $drinkCommand; //將命令發送者綁定到命令接收器 public function addCommand(Command $mealCommand, Command $drinkCommand){ $this->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\r\n"; } } class playerOutput implements Process { public function process(){ echo "output\r\n"; } }
對於播放器的各類功能,這裏是開放的。只要你遵照約定,實現了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
依賴倒置簡單地講就是將依賴關係倒置爲依賴接口,具體概念以下:
爲何要依賴接口?由於接口體現對問題的抽象,同時因爲抽象通常是相對穩定的或者是相對變化不頻繁的,而具體是易變的。所以,依賴抽象是實現代碼擴展和運行期內綁定(多態)的基礎:只要實現了該抽象類的子類,均可以被類的使用者使用。
<?php interface employee { public function working(); } class teacher implements employee { public function working(){ echo 'teaching...'; } } class coder implements employee { public function working(){ echo 'coding...'; } } class workA { public function work(){ $teacher = new teacher; $teacher->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的兩種實現。