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

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

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

一、概述

代理模式(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 条评论
登录 后参与评论

相关文章

来自专栏Golang语言社区

Go语言轻量级线程Goroutine用法实例

本文实例讲述了Go语言轻量级线程Goroutine用法。分享给大家供大家参考。具体如下: goroutine 是由 Go 运行时环境管理的轻量级线程。 go f...

33511
来自专栏码农之路_构建基础

基于汇编的 C/C++ 协程 - 切换上下文

既然本系列讲的是基于汇编的 C/C++ 协程,那么这篇文章我们就来讲讲使用汇编来进行上下文切换的原理。

1906
来自专栏猿人谷

用C来实现内存池

介绍:        设计内存池的目标是为了保证服务器长时间高效的运行,通过对申请空间小而申请频繁的对象进行有效管理,减少内存碎片的产生,合理分配管理用户内存,...

2456
来自专栏从零开始学自动化测试

python接口自动化17-响应时间与超时(timeout)

前言 requests发请求时,接口的响应时间,也是我们需要关注的一个点,如果响应时间太长,也是不合理的。 如果服务端没及时响应,也不能一直等着,可以设置一个t...

3096
来自专栏ml

JavaScript基础知识(1)

表单的确认 :       客户端确认         --减少服务器负载         --缩短用户等待时间         --兼容性难       服务...

2603
来自专栏desperate633

web开发中 web 容器的作用(如tomcat)什么是web容器?web容器的作用容器如何处理请求URL与servlet映射模式

我们讲到servlet可以理解服务器端处理数据的java小程序,那么谁来负责管理servlet呢?这时候我们就要用到web容器。它帮助我们管理着servlet等...

742
来自专栏racaljk

Boost Coroutine2 - stackful coroutine简介

协程可以很轻量的在子例程中进行切换,它由程序员进行子例程的调度(即切换)而不像线程那样需要内核参与,同时也省去了内核线程切换的开销,因为一个协程切换保留的就是函...

833
来自专栏贺贺的前端工程师之路

Angualr2 之 angular模块Angular 模块化提供服务特性模块 - 业务上的最佳实践(n)共享模块XxxModule.forRoot配置核心服务知识点

Angular 模块是带有 @NgModule 装饰器的函数。 @NgModule接收一个元数据对象,该对象告诉 Angular 如何编译和运行模块代码。

1053
来自专栏JMCui

Redis学习一(基础入门).

一、前言     Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、key-Value 的数据库、并提供多...

3635
来自专栏fixzd

redis系列:通过队列案例学习list命令

这一篇文章将讲述Redis中的list类型命令,同样也是通过demo来讲述,其他部分这里就不在赘述了。

971

扫描关注云+社区