The proxy pattern is a structural design pattern, targeting the classic structure in which classes and objects are combined. The proxy pattern is also a frequently used design pattern that we need to focus on. It can add some additional functions without changing the target object.
Definition
Proxy mode (Proxy) provides a proxy for other objects to control access to this object. Use the proxy pattern to create a proxy object, let the proxy object control access to the target object (the target object can be a remote object, an object that is expensive to create, or an object that requires security control), and you can add some extras without changing the target object. function.
Question
Currently, the system has a Login class for user login and registration business. The pseudo code is as follows:
class UserLogin { // …… 省略属性和部分方法 public function login ($name, $pass) { // 登录业务 } public function reg ($name, $pass) { // 注册业务 } }
Now, we want to add a function to the user login and registration business - current limiting, so that the client can limit the frequency of calling this method to a maximum of 5 times per second. Now, let's implement this function. The pseudo code is as follows:
class UserLogin { // …… 省略属性和部分方法 public function login ($name, $pass) { // 限流 $limit = new Limit(); if ($limit->restrict()) { // …… } // 登录业务 } public function reg ($name, $pass) { // 限流 $limit = new Limit(); if ($limit->restrict()) { // …… } // 注册业务 } }
Let's take a look at the above code. It has several problems. First, the current limiting code invades the business code and is highly coupled with the business code. Secondly, current limiting has nothing to do with business code and violates the single responsibility principle.
Implementation
Next, we rewrite the above code using proxy mode. The rewritten code is as follows:
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); } }
The above method is based on the design idea of interface rather than implementation programming, but if the original class does not define an interface, or this class is not developed and maintained by us, then how to implement the proxy mode?
For the extension of this external class, we generally use the inheritance method to achieve it.
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); } }
Take a look at the above code. Are there any problems? You will find that this similar code
if ($this->limit->restrict()) { // ... }
appears twice. Now we just add the current limiting function to two methods. If the UserLogin class has 10 methods, and we want to add the current limiting function to each method, then we need to copy this code 10 times. If we want to add current limiting function to all methods in 10 classes, and each class has 10 methods, then the above current limiting code will be repeated 100 times.
Of course, you will say that I can encapsulate the current limiting code into a function to solve the above problem? But there is still a problem that cannot be solved. Every method in the original class must be re-implemented in the proxy class. Just like there are reg and login methods in the original class above, there are also reg and login methods in the proxy class.
Dynamic proxy
How to solve the above problems, we can use dynamic proxy to solve it. If you want to use dynamic proxy, you must understand and use the reflection mechanism in PHP.
php has a complete reflection API, adding the ability to reverse engineer classes, interfaces, functions, methods and extensions. Additionally, the Reflection API provides methods to extract documentation comments from functions, classes, and methods. Regarding the knowledge related to PHP reflection, I will not go into details here. You can check the relevant information by yourself.
Note that using reflection consumes a lot of performance, so please do not use it under normal circumstances.
Let’s show how to use reflection to implement dynamic proxy. The pseudo code is as follows:
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; } } } } }
The test code is as follows:
$login = new Login(); $loginProxy = new LimitProxy($login); $loginProxy->reg('gwx', '111111'); $loginProxy->login('james', '111111111');
Application scenario
Access control (protection agent). For example, the system has an order module. Originally, this module also had permission control, but now we want it to be accessible only to clients with specified IP addresses. Then we can use proxy mode.
Local execution of remote services (remote proxy) is suitable for situations where the service object is located on a remote server.
Develop some non-functional requirements in the business code, such as: current limiting, statistics, logging
Cache applications, For example, add a cache proxy. When the cache exists, the cache proxy is called to obtain the cached data. When the cache does not exist, the original interface is called.
The above is the detailed content of Understand the proxy pattern of PHP design patterns in one article. For more information, please follow other related articles on the PHP Chinese website!