在傳統的OOP(面向對象編程:Object-Oriented Programming)思想裏,一般把應用程序分解成若干個的對象,強調高內聚,弱耦合,從而提高應用程序的模塊化程度,但是在處理某些問題的時候,OOP會顯得不夠靈活,比如說,應用程序裏很多業務邏輯都要在操作之初進行“權限檢查”,在操作之後進行“日誌記錄”,如果直接把處理這些操作的代碼加入到每個模塊中,那麼無疑破壞了OOP的“單一職責”原則,模塊的可重用性會大大降低,這時候傳統的OOP設計往往採取的策略是加入相應的代理(Proxy)層來完成系統的功能要求,但這樣的處理明顯使系統整體增加了一個層次的劃分,複雜性也隨之增加,從而給人過於厚重的感覺。正是爲了處理這樣的問題,AOP(面向方面編程:Aspect-Oriented
Programming)思想應運而生了,假設把應用程序想成一個立體結構的話,OOP的利刃是縱向切入系統,把系統劃分爲很多個模塊(如:用戶模塊,文章模塊等等),而AOP的利刃是橫向切入系統,提取各個模塊可能都要重複操作的部分(如:權限檢查,日誌記錄等等)。由此可見,AOP是OOP的一個有效補充。
就目前的PHP來說,還沒有一個完整的AOP內置實現,雖然出現了RunKit,但一直都以BETA的狀態呆在PECL項目裏,估計很長時間內不太可能成爲PHP的缺省設置。那是不是AOP在PHP裏就破滅了呢?當然不是,因爲我們有__get(),__set(),__call()等魔術方法,合理使用這些方法可以爲我們實現某種程度的“準AOP”能力,之所以說是準AOP,是因爲單單從實現上來看,稱其爲AOP有些牽強,但是從效果上來看,又部分實現了AOP的作用,雖然其實現方式並不完美,但對於一般的使用已經足夠了。特別是從PHP4.3.0開始,這些魔術方法已經成爲了PHP的缺省內置實現,掃除了PHP4/5兼容的顧慮,那麼就更加沒
<?php //應用程序中某個業務邏輯類 class BIZ { public function foobar() { echo '業務邏輯<br />'; } } //業務邏輯類的包裝類 class AOP { private $instance; public function __construct($instance) { $this->instance = $instance; } public function __call($method, $argument) { if(! method_exists($this->instance, $method)) { throw new Exception('未定義的方法:' . $method); } echo '權限檢查<br />'; $callBack = array($this->instance, $method); $return = call_user_func_array($callBack, $argument); echo '日誌記錄<br />'; return $return; } } //工廠方法 class Factory { public function getBizInstance() { return new AOP(new BIZ()); } } //客戶端調用演示 header("Content-Type: text/html; charset=gbk"); try { $obj = Factory::getBizInstance(); $obj->foobar(); } catch(Exception $e) { echo 'Caught exception: ', $e->getMessage(); } ?>
有理由不使用它們。這裏要說明的是PHP5對這些魔術方法的實現有些許的不同,下面偶們舉例說明:
屏幕顯示:
權限檢查
業務邏輯
日誌記錄
總結:
整個的實現思路其實很簡單,關鍵就是客戶端請求的對象不能直接實例化出來,而是利用工廠方法返回一個請求對象的包裝對象,在包裝對象內利用魔術方法來處理權限處理,日誌記錄等公共操作。這既是巧妙的地方,也是最有可能出問題的地方,因爲客戶端使用對象並不是它想象中的對象,而是一個包裝後的對象,比如說,客戶端通過getBizInstance()方法以爲得到的對象是BIZ,但實際上它得到的是一個BIZ的包裝對象AOP,這樣的話,如果客戶端進行一些諸如get_class()之類和對象類型相關的操作就會出錯,當然,大多數情況下,客戶端似乎不太會做類似的操作。