站長資訊網
最全最豐富的資訊網站

一文讀懂php設計模式之代理模式

代理模式屬于結構性設計模式,針對類與對象組合在一起的經典結構。代理模式也是一種使用較多的設計模式,需要我們重點掌握,他可以在不改變目標對象的情況下,添加一些額外的功能。

定義

代理模式(Proxy)為其他對象提供一種代理以控制對這個對象的訪問。使用代理模式創建代理對象,讓代理對象控制目標對象的訪問(目標對象可以是遠程的對象、創建開銷大的對象或需要安全控制的對象),并且可以在不改變目標對象的情況下添加一些額外的功能。

問題

目前系統關于用戶登錄注冊的業務,有一個Login類。偽代碼如下:

class UserLogin {     // …… 省略屬性和部分方法          public function login ($name, $pass)     {         // 登錄業務     }          public function reg ($name, $pass)     {         // 注冊業務     } }

現在,我們想在用戶登錄和注冊的業務中添加一個功能——限流,讓客戶端調用該方法的頻次限制在一秒鐘最多5次?,F在,我們來實現該功能,偽代碼如下:

class UserLogin {     // …… 省略屬性和部分方法          public function login ($name, $pass)     {         // 限流         $limit = new Limit();         if ($limit->restrict()) {             // ……         }                  // 登錄業務     }          public function reg ($name, $pass)     {         // 限流         $limit = new Limit();         if ($limit->restrict()) {             // ……         }                  // 注冊業務     } }

大家看看上面的代碼,它有幾個問題,首先,限流代碼侵入到業務代碼中,跟業務代碼高度耦合。其次,限流和業務代碼無關,違背單一職責原則。

實現

接下來,我們用代理模式重寫上面的代碼,重寫后的代碼如下所示:

interface IUserLogin {     function login ();     function register (); }  class UserLogin implements IUserLogin {     // …… 省略屬性和部分方法          public function reg ($uname, $pass)     {         // 注冊業務     }          public function login ($uname, $pass)     {         // 登錄業務     } }  class UserLoginProxy implements IUserLogin {     private $limit = null;     private $login = null;          public function __construct(Limit $limit, Login $login)     {         $this->limit = $limit;         $this->login = $login;     }          public function login($uname, $pass)     {         if ($this->limit->restrict()) {             // ...         }         return $this->login->login($uname, $pass);     }          public function register($uname, $pass)     {         if ($this->limit->restrict()) {             // ...         }         return $this->login->register($uname, $pass);     } }

上面的方法是基于接口而非實現編程的設計思想,但如果原始類并沒有定義接口,或者這個類并不是我們開發和維護的,那么要怎么實現代理模式呢?

對于這種外部類的擴展,我們一般采用繼承的方法來實現。

class UserLogin {     public function reg ($uname, $pass)     {         // 注冊業務     }          public function login ($uname, $pass)     {         // 登錄業務     } }  class UserLoginProxy extends Login {     private $limit = null;          public function __construct(Limit $limit, Login $login)     {         $this->limit = $limit;         $this->login = $login;     }          public function login($uname, $pass)     {         if ($this->limit->restrict()) {             // ...         }         return parent::login($uname, $pass);     }          public function reg($uname, $pass)     {         if ($this->limit->restrict()) {             // ...         }         return parent::register($uname, $pass);     } }

大家看看上面的代碼,是不是還有什么問題?你會發現

if ($this->limit->restrict()) {     // ... }

這段相似的代碼,出現了兩次。現在我們只是給兩個方法添加了限流功能,如果UserLogin類有10個方法,每個方法我們都想要添加限流的功能,那么我們就需要重復復制10次該段代碼。如果,我們想要給10給類中所有方法都添加限流功能,每個類中都有10個方法,那么上面的限流代碼將會重復100次。

當然,你會說我可以將限流的代碼封裝到一個函數里不就解決了上述問題么?但還有一個問題解決不了,原始類里每個方法在代理類中都要重新實現一遍。就像上面原始類里有reg、login方法,代理類里也有reg、login方法。

動態代理

如何解決上述的問題,我們可以借助動態代理來解決。想要使用動態代理,就要理解并使用PHP中的反射機制。

php具有完整的反射 API,添加了對類、接口、函數、方法和擴展進行反向工程的能力。 此外,反射 API 提供了方法來取出函數、類和方法中的文檔注釋。關于php的反射相關知識,這里就不詳述了,大家可以自行查閱相關信息。

注意,使用反射對性能消耗很大,一般情況下請不要使用。

下面我們來展示如何用反射實現動態代理,偽代碼如下:

class UserLogin {     public function reg ($uname, $pass)     {         // 注冊業務         echo '注冊業務' . PHP_EOL;     }     public function login ($uname, $pass)     {         // 登錄業務         echo '登錄業務' . PHP_EOL;     } } class LimitProxy {     // 用來保存多個實例對象     private $target = [];     public function __construct(Object $obj)     {         $this->target[] = $obj;     }     public function __call($name, $arguments)     {         foreach ($this->target as $obj) {             $ref = new ReflectionClass($obj);             if ($method = $ref->getMethod($name)) {                 if ($method->isPublic() && !$method->isAbstract()) {                     // 限流                     echo "這里是限流業務處理" . PHP_EOL;                     $result = $method->isStatic() ? $method->invoke(null, $obj, ...$arguments) : $method->invoke($obj, ...$arguments);                     return $result;                 }             }         }     } }

測試代碼如下:

$login = new Login(); $loginProxy = new LimitProxy($login); $loginProxy->reg('gwx', '111111'); $loginProxy->login('james', '111111111');

應用場景

  • 訪問控制 (保護代理)。 比如系統有一個訂單的模塊,原本該模塊也有權限控制,但現在我們希望只針對指定ip的客戶端可以訪問,那么我們可以使用代理模式。

  • 本地執行遠程服務 (遠程代理)適用于服務對象位于遠程服務器上的情形。

  • 在業務代碼中開發一些非功能性的需求,比如:限流、統計、日志記錄

  • 緩存方面的應用,比如添加一個緩存代理,當緩存存在時,就調用緩存代理獲取緩存的數據,當緩存不存在時,就調用原始接口。

贊(0)
分享到: 更多 (0)
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
久久久影院亚洲精品| 久久91这里精品国产2020| 中文字幕乱码亚洲精品一区| 久久久精品波多野结衣AV| 99久久精品费精品国产一区二区| 日韩精品一区二区三区在线观看l 日韩精品一区二区三区毛片 | 最新国产午夜精品视频不卡| 国产精品福利在线观看| 91精品视频播放| 国产成人精品日本亚洲网站| 中文字幕日韩精品有码视频| 国产精品免费在线播放| 一本之道av不卡精品| 日韩av无码久久精品免费| 国产亚洲精品美女久久久久久下载 | 亚洲国产成人精品久久久国产成人一区二区三区综 | 91亚洲国产成人久久精品| 亚洲国产精品自在线一区二区| 久久99精品国产99久久6男男| 熟女人妻少妇精品视频| 九九热这里只有国产精品| 精品人妻V?出轨中文字幕| 国产精品女同一区二区久久| 亚洲精品无码永久在线观看| 九九精品国产99精品| 四虎精品影库4HUTV四虎| 精品无码成人片一区二区| 日韩加勒比一本无码精品| 欧美日韩综合一区二区三区| 免费在线观看日韩| 在线观看91精品国产网站| 青草午夜精品视频在线观看| 日韩AV无码精品一二三区| 青青热久久国产久精品 | 韩国精品福利一区二区三区| 精品亚洲av无码一区二区柚蜜| 亚洲精品无码久久久久秋霞| 无码人妻精品内射一二三AV| 亚洲精品国产第一综合99久久| 国产精品无码一区二区在线观| 蜜臀亚洲AV无码精品国产午夜. |