设计模式专题(四)——代理模式

设计模式专题(四)——代理模式

(原创内容,转载请注明来源,谢谢)

一、概述

代理模式(Proxy)是为其他对象提供一种代理,以控制这个对象的访问。即外系统需要调用系统内部的服务,都要通过代理。这个模式在RPC架构中非常常用。

1)使用场景

代理模式在项目中使用广泛。

1.远程代理。即将proxy文件放置于公共部分,作为真正提供接口的地方,而实际实现接口不在此文件中。这样实现远程访问的功能。

2.虚拟代理。如果需要创建一个开销很大的对象,可以用代理模式,用它来存放实例化需要很长时间的真实对象。例如打开一个网页,里面有大量图片,通常页面会先加载出来,图片再慢慢的刷出来,这就是用到了代理模式。因此,代理模式通常会结合单例模式。

3.安全代理。通过代理模式用来控制真实对象访问的权限,把需要提供的接口对外提供,而不是提供全部的功能。

4.智能指引。当调用真实的对象,代理处理另外一件事情。例如检查对象是否是持久对象,检查对象是否已经锁定,检查对象是否没有使用可以释放。

2)意义

1.保密性

代理模式,通过引入一个新的对象,来实现对真实对象的操作或者将新的对象作为真实对象的一个替身。它可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。

2. 延迟加载

当还没使用一个类的实例时,先实例化的是代理类,然后在真正使用的时候才去实例化正式的类。因此,延迟加载主要有两个意义:首先,它可以在时间轴上分散系统压力,尤其在系统启动时,不必完成所有的初始化工作,从而加速启动时间;其次,对很多真实场景而言,在软件启动直到被关闭的整个过程中,可能根本不会被调用,初始化这些数据无疑是一种资源浪费。例如框架在执行的时候会加载很多的代理,应对各种操作(例如各个子系统的调用),不同的操作会触发不同的代理,但是对于具体的某种操作只会触发部分的代理而不会触发全部代理,因此就可以起到节约时间的作用。

3. 动态代理

动态代理是指不必为每一个真实的服务文件写一个代理类,而是只写一个类,通常可以通过魔术方法__call来实现动态代理。

二、UML类图

三、功能实现

1)业务场景

使用代理模式对外部提供服务接口,实现动态代理,对外部进开放proxy。现对外提供若干字符串处理服务,现假设有字符串加密校验、多维数组替换字段。

2)实现逻辑

1.抽象一个类,用于给其他类继承。

2.实现proxy类,作为代理模式的核心,作为代理,其中定义__call方法给其他代码动态调用。

3.实现service类,完成具体的功能。

4.实现client类,调用proxy,实现接口调用。

3)代码实现

<?php
//代理模式
//抽象类
abstract class StringDealer{
         public static functiongetInstance(){}
}
//client,调用proxy获取结果
class StringDealerClient{
         private $strToDeal;
         public function__construct($str){
                   $this->strToDeal= $str;
                   return$this;
         }
         public function__call($name, $args=''){
                   if(empty($args)){
                            $args= $this->strToDeal;
                   }
                   $strProxy =StringDealerProxy::getInstance();
                   return$strProxy->$name($args);
         }
}
//proxy
class StringDealerProxy extends StringDealer{
         private static $ins;//代理自己的的实例
         private $svcIns;//服务StringDealerService的实例
         private function__construct(){}
         private function__clone(){}
         public static functiongetInstance(){
                   if(null ==self::$ins || !(self::$ins instanceof self)){
                            self::$ins= new StringDealerProxy();
                   }
                   returnself::$ins;
         }
         public function__call($name, $args){
                   if(null ==$this->svcIns || !($this->svcIns instanceof $StringDealerService)){
                            $this->svcIns= new StringDealerService();
                   }       
                   //判断类的方法是否存在,不存在则返回null
                   if(!method_exists($this->svcIns,$name)){
                            returnnull;
                   }
                   //存在则执行方法返回结果
                   return$this->svcIns->$name($args);
         }       
}
//实现类
class StringDealerService extends StringDealer{
         public functionsecretCheck($str){
                   $str =$str[0];
                   $str =md5($str. 'stringdealer');
                   //调用数据库获取加密后的字段,省略代码
                   $sqlStr ='xx';
                   return $str== $sqlStr;
         }
         public functionmutiArrayReplace($strToRep, $strReped, $arr){
                   if(!is_array($arr)){
                            returnstr_replace($strToRep, $strReped, $arr);
                   }
                   //含有特殊字符,无法将数组转成json进行字符串替换
                   if(in_array($strToRep,array("'", '"', '(', ')', '[', ']', '{', '}', ':'))){
                            foreach($arras &$item){
                                     if(is_array($item)){
                                               $item= $this->mutiArrayReplace($strToRep, $strReped, $item);
                                     }else{
                                               $item= str_replace($strToRep, $strReped, $item);
                                     }
                            }
                   }else{
                            $arrStr= json_encode($arr);
                            $arrStr= str_replace($strToRep, $strReped, $arrStr);
                            $arr= json_decode($arrStr);
                   }
                   return$arrStr;
         }
}
$strDealer = new StringDealerClient('abcd');
echo $strDealer->secretCheck();

——written by linhxx 2017.07.29

相关阅读:

设计模式专题(三)——装饰模式

设计模式专题(二)——策略模式

设计模式专题(一)——面向对象的设计原则

原文发布于微信公众号 - 决胜机器学习(phpthinker)

原文发表时间:2017-07-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序人生

数据的存储

这是我今年一月份在 team 内部的一次分享。介绍了主流的数据存储方案,包括:内存,文件,数据库和消息队列,以及数据序列化/反序列化的方法。很多时候,工具就在那...

3476
来自专栏IMWeb前端团队

基于flux和observer相结合的思想的数据管理器

Redux和MobX是前端领域最前沿的两个状态管理library,前者遵循Flux思想,后者独树一帜,不过可以认为继承了observer思想。在具体本文的阐述前...

1846
来自专栏我是攻城师

分布式日志收集之Logstash 笔记(二)

2886
来自专栏技术分享

.NET/ASP.NETMVC 深入剖析 Model元数据、HtmlHelper、自定义模板、模板的装饰者模式(二)

阅读目录: 4.ModelMetadata(ModelMetadata元数据如何支撑Model与View之间的组合关系) 4.1.ModelMetadata元...

2435
来自专栏QQ音乐前端团队专栏

【译】JavaScript与WebAssembly进行比较+在哪些情况下会优于JavaScript

这次我们来分析WebAssembly的工作原理,以及在如下几个方面和JavaScript进行比较:加载时间,执行速度,垃圾回收,内存使用情况,平台API访问,调...

2924
来自专栏沈唁志

浅谈PHP中的设计模式

983
来自专栏程序员宝库

我所理解的接口设计

前言 自己做接口开发的时间也算不短了(三年),想写这篇文章其实差不多已经有一年多的时间了。我将从下面的方向来对我所理解的接口设计做个总结: 接口参数定义 -> ...

2887
来自专栏小狼的世界

使用Jsonp解决跨域数据访问问题

符合Web2.0特征的众多网站一个明显的特点就是采用Ajax。Ajax提供了在后台提交请求访问数据的功能。其实现主要使用的是XMLHttpRequest函数,...

872
来自专栏QQ音乐技术团队的专栏

使用 Jest 进行前端单元测试

目前 Jest 已经在 Facebook 开源的 React, React Native 等前端项目中被做为标配测试框架。下面简单介绍一些 Jest 比较有用的...

5549
来自专栏Java Web

模仿天猫实战【SSM】——总结

前台花费了大部分的时间,不仅仅是繁杂的样式和页面需要自己去编写,业务逻辑也比后台要复杂一些,因为是模仿,所以大部分的 CSS 我都是参照着天猫官网写的(利用Fi...

48910

扫码关注云+社区