PHP利用魔術方法實現準AOP

在傳統的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()之類和對象類型相關的操作就會出錯,當然,大多數情況下,客戶端似乎不太會做類似的操作。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章