PHP面向?qū)ο笪宕笤瓌t之單一職責(zé)原則(SRP)詳解
本文實例講述了PHP面向?qū)ο笪宕笤瓌t之單一職責(zé)原則(SRP)。分享給大家供大家參考,具體如下:
單一職責(zé)原則(Single Pesponsibility Principle, SRP)
單一職責(zé)有兩個含義: 一個是避免相同的職責(zé)分散到不同的類中, 別一個是避免一個類承擔(dān)太多職責(zé)
為什么要遵守SRP呢?
(1)可以減少類之間的耦合
如果減少類之間的耦合,當(dāng)需求變化時,只修改一個類,從而也就隔離了變化;如果一個類有多個不同職責(zé),它們耦合在一起,當(dāng)一個職責(zé)發(fā)生變化時,可能會影響到其他職責(zé)。
(2)提高類的復(fù)用性
修改電腦比修理電視機簡單多了。主要原因就在于電視機各個部件之間的耦合性太高,而電腦則不同,電腦的內(nèi)存、硬盤、聲卡、網(wǎng)卡、鍵盤燈等部件都可以很容易地單獨拆卸和組裝。某個部件壞了,換上新的即可。上面的例子就體現(xiàn)了單一職責(zé)的優(yōu)勢。由于使用了單一職責(zé),使得‘組件'可以方便地‘拆卸'和‘組裝'。
不遵守SRP會影響對類的復(fù)用性。當(dāng)只需要用該類的某一個職責(zé)時,由于它和其他的職責(zé)耦合在一起,也就很難分離出。
遵守SRP在實際代碼開發(fā)中有沒有什么應(yīng)用?有的。以數(shù)據(jù)持久層為例,所謂的數(shù)據(jù)持久層主要指的是數(shù)據(jù)庫操作,當(dāng)然,還包括緩存管理等。這時就需要數(shù)據(jù)持久層支持多種數(shù)據(jù)庫。應(yīng)該怎么做?定義多個數(shù)據(jù)庫操作類?想法已經(jīng)很接近了,再進(jìn)一步,就是使用工廠模式。
工廠模式(Faction)允許你在代碼執(zhí)行時實例化對象。它之所以被稱為工廠模式是因為它負(fù)責(zé)‘生產(chǎn)對象'。以數(shù)據(jù)庫為例,工廠需要的就是根據(jù)不同的參數(shù),生成不同的實例化對象。最簡單的工廠就是根據(jù)傳入的類型名實例化對象,如傳入MySQL,就調(diào)用MySQL類并實例化,如果是SQLite,則調(diào)用 SQLite的類并實例化,甚至還可以處理TXT、Execl等‘類數(shù)據(jù)庫'。
工廠類也就是這樣的一個類,它只負(fù)責(zé)生產(chǎn)對象,而不負(fù)責(zé)對象的具體內(nèi)容。
以下是示例
定義一個適配器的接口
interface Db_Adpater { /** * 數(shù)據(jù)庫連接 * @param $config 數(shù)據(jù)庫配置 * @return mixed resource */ public function connect($config); /** * 執(zhí)行數(shù)據(jù)庫查詢 * @param $query 數(shù)據(jù)庫查詢的SQL字符串 * @param $handle 連接對象 * @return mixed */ public function query($query,$handle); }
定義一個實現(xiàn)了DB_Adpater接口的MySQL數(shù)據(jù)庫操作類
class Db_Adapter_Mysql implements Db_Adpater { private $_dbLink; //數(shù)據(jù)庫連接字符串標(biāo)識 /** * 數(shù)據(jù)庫連接函數(shù) * @param $config 數(shù)據(jù)庫配置 * @return resource * @throws Db_Exception */ public function connect($config) { if($this->_dbLink = @mysql_connect($config->host . (empty($config->port) ? '' : ':' . $config->prot) ,$config->user, $config->password, true)) { if(@mysql_select_db($config->database, $this->_dbLink)) { if($config->charset) { mysql_query("SET NAME '{$config->charset}'", $this->_dbLink); } return $this->_dbLink; } } throw new Db_Exception(@mysql_error($this->_dbLink)); } /** * 執(zhí)行數(shù)據(jù)庫查詢 * @param $query 數(shù)據(jù)庫查詢SQL字符串 * @param $handle 連接對象 * @return resource */ public function query($query,$handle) { if($resource = @mysql_query($query,$handle)) return $resource; } }
定義一個實現(xiàn)了DB_Adpater接口的SQLite數(shù)據(jù)庫操作類
class Db_Adapter_sqlite implements Db_Adpater { private $_dbLink; //數(shù)據(jù)庫連接字符串標(biāo)識 public function connect($config) { if($this->_dbLink = sqlite_open($config->file, 0666, $error)) { return $this->_dbLink; } throw new Db_Exception($error); } public function query($query, $handle) { if($resource = @sqlite_query($query,$handle)) { return $resource; } } }
現(xiàn)在如果需要一個數(shù)據(jù)庫操作的方法怎么做,只需定義一個工廠類,根據(jù)傳入不同的生成需要的類即可
class sqlFactory { public static function factory($type) { if(include_once 'Drivers/' . $type . '.php') { $classname = 'Db_Adapter_'.$type; return new $classname; } else throw new Exception('Driver not found'); } }
調(diào)用時,就可以這么寫
$db = sqlFactory::factory('MySQL'); $db = sqlFactory::factory('SQLite');
我們把創(chuàng)建數(shù)據(jù)庫連接這塊程序單獨拿出來,程序中的CURD就不用關(guān)心什么數(shù)據(jù)庫了,只要按照規(guī)范使用對應(yīng)的方法即可。
工廠方法讓具體的對象解脫出來,使其不再依賴具體的類,而是抽象。
設(shè)計模式里面的命令模式也是SRP的體現(xiàn),命令模式分離“命令的請求者”和“命令的實現(xiàn)者”方面的職責(zé)。舉一個很好理解的例子,就是你去餐館訂餐吃飯,餐館存在顧客、服務(wù)員、廚師三個角色。作為顧客,你要列出菜單,傳給服務(wù)員,由服務(wù)員通知廚師去實現(xiàn)。作為服務(wù)員,只需要調(diào)用準(zhǔn)備飯菜這個方法(對廚師喊“該炒菜了”),廚師聽到要炒菜的請求,就立即去做飯。在這里,命令的請求和實現(xiàn)就完成了解耦。
模擬這個過程,首先定義廚師角色,廚師進(jìn)行實際做飯、燒湯的工作。
以下是示例
/** * 廚師類,命令接受者與執(zhí)行者 * Class cook */ 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(); }
輪到服務(wù)員出場,服務(wù)員是命令的傳送者,通常你到飯館吃飯都是叫服務(wù)員吧,不能直接叫廚師,一般都是叫“服務(wù)員,給我來盤番茄炒西紅柿”。所以,服務(wù)員是顧客和廚師之間的命令溝通都。
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(); } }
現(xiàn)在顧客可以按照菜單叫服務(wù)員了
class cookControl { private $mealcommand; private $drinkcommand; //將命令發(fā)送者綁定以命令接收器上面來 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(); } }
好了,現(xiàn)在完成整個過程
$control = new cookControl; $cook = new cook; $mealcommand = new MealCommand($cook); $drinkcommand = new DrinkCommand($cook); $control->addCommand($mealcommand,$drinkcommand); $control->callmeal(); $control->calldrink();
從上面的例子可以看出,原來設(shè)計模式并非純理論的東西,而是來源于實際生活,就連普通的餐館老板都懂設(shè)計模式這門看似高深的學(xué)問。其實,在經(jīng)濟和管理活動中對流程的優(yōu)化就是對各種設(shè)計模式的摸索和實踐。所以,設(shè)計模式并非計算機編程中的專利。事實上,設(shè)計模式的起源并不是計算機,而是源于建筑學(xué)。
在設(shè)計模式方面,不僅以上這兩種體現(xiàn)了SRP,還有別的(比如代理模式)也體現(xiàn)了SRP。SRP不只是對類設(shè)計有意義,對以模塊、子系統(tǒng)為單位的系統(tǒng)架構(gòu)設(shè)計同樣有意義。
模塊、子系統(tǒng)也應(yīng)該僅有一個引起它變化的原因,如MVC所倡導(dǎo)的各個層之間的相互分離就是SRP在系統(tǒng)總體設(shè)計中的應(yīng)用。
SRP是最簡單的原則之一,也是最難做好的原則之一。我們會很自然地將職責(zé)連接在一起。找到并且分離這些職責(zé)是軟件設(shè)計需要達(dá)到的目的
一些簡單的應(yīng)用遵循的做法如下:
根據(jù)業(yè)務(wù)流程,把業(yè)務(wù)對象提煉出來。如果業(yè)務(wù)的流程的鏈路太復(fù)雜,就把這個業(yè)務(wù)對象分離為多個單一業(yè)務(wù)對象。當(dāng)業(yè)務(wù)鏈標(biāo)準(zhǔn)化后,對業(yè)務(wù)對象的內(nèi)部情況做進(jìn)一步處理,把第一次標(biāo)準(zhǔn)化視為最高層抽象,第二次視為次高層抽象,以此類推,直到“恰如其分”的設(shè)計層次
職責(zé)的分類需要注意。有業(yè)務(wù)職責(zé),還要有脫離業(yè)務(wù)的抽象職責(zé),從認(rèn)識業(yè)務(wù)到抽象算法是一個層層遞進(jìn)的過程。就好比命令模式中的顧客,服務(wù)員和廚師的職責(zé),作為老板(即設(shè)計師)的你需要規(guī)劃好各自的職責(zé)范圍,即要防止越俎代庖,也要防止互相推諉。
更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php面向?qū)ο蟪绦蛟O(shè)計入門教程》、《PHP數(shù)組(Array)操作技巧大全》、《PHP基本語法入門教程》、《PHP運算與運算符用法總結(jié)》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》
希望本文所述對大家PHP程序設(shè)計有所幫助。
相關(guān)文章
深入Memcache的Session數(shù)據(jù)的多服務(wù)器共享詳解
本篇文章是對Memcache的Session數(shù)據(jù)的多服務(wù)器共享進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06php通過sort()函數(shù)給數(shù)組排序的方法
這篇文章主要介紹了php通過sort()函數(shù)給數(shù)組排序的方法,實例分析了php中sort()函數(shù)的功能及相關(guān)使用技巧,非常具有實用價值,需要的朋友可以參考下2015-03-03解析PHP中VC6 X86和VC9 X86的區(qū)別及 Non Thread Safe的意思
本篇文章是對PHP中VC6 X86和VC9 X86的區(qū)別及 Non Thread Safe的意思進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06PHP API接口必備之輸出json格式數(shù)據(jù)示例代碼
這篇文章主要給大家介紹了關(guān)于PHP API接口必備之輸出json格式數(shù)據(jù)的相關(guān)資料文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。2017-06-06