加入收藏 | 设为首页 | 会员中心 | 我要投稿 云计算网_泰州站长网 (http://www.0523zz.com/)- 视觉智能、AI应用、CDN、行业物联网、智能数字人!
当前位置: 首页 > 站长学院 > PHP教程 > 正文

一文认识php设计模式之代理模式

发布时间:2022-07-21 13:42:49 所属栏目:PHP教程 来源:互联网
导读:代理模式属于结构性设计模式,针对类与对象组合在一起的经典结构。代理模式也是一种使用较多的设计模式,需要我们重点掌握,他可以在不改变目标对象的情况下,添加一些额外的功能。 定义 代理模式(Proxy)为其他对象提供一种代理以控制对这个对象的访问。使
  代理模式属于结构性设计模式,针对类与对象组合在一起的经典结构。代理模式也是一种使用较多的设计模式,需要我们重点掌握,他可以在不改变目标对象的情况下,添加一些额外的功能。
  定义
 
  代理模式(Proxy)为其他对象提供一种代理以控制对这个对象的访问。使用代理模式创建代理对象,让代理对象控制目标对象的访问(目标对象可以是远程的对象、创建开销大的对象或需要安全控制的对象),并且可以在不改变目标对象的情况下添加一些额外的功能。
 
  问题
 
  目前系统关于用户登录注册的业务,有一个Login类。伪代码如下:
 
  class UserLogin
 
  {
 
      // …… 省略属性和部分方法
 
       
 
      public function login ($name, $pass)
 
 
      public function reg ($name, $pass)
 
      {
 
          // 注册业务
 
      }
 
  }
 
  现在,我们想在用户登录和注册的业务中添加一个功能——限流,让客户端调用该方法的频次限制在一秒钟最多5次。现在,我们来实现该功能,伪代码如下:
 
  {
 
      // …… 省略属性和部分方法
 
       
 
      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 login ($uname, $pass)
 
      {
 
          // 登录业务
 
      }
 
  }
 
   
 
  class UserLoginProxy implements IUserLogin
 
  {
 
          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方法。
 
  下面我们来展示如何用反射实现动态代理,伪代码如下:
 
 
 
  class UserLogin
 
  {
 
      {
 
          $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的客户端可以访问,那么我们可以使用代理模式。
 
  本地执行远程服务 (远程代理)适用于服务对象位于远程服务器上的情形。
 
  在业务代码中开发一些非功能性的需求,比如:限流、统计、日志记录
 
  缓存方面的应用,比如添加一个缓存代理,当缓存存在时,就调用缓存代理获取缓存的数据,当缓存不存在时,就调用原始接口。

(编辑:云计算网_泰州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读