读书笔记-设计模式-可复用版-Proxy 代理模式

发表于2019-06-20
评论1 4.4k浏览

读书笔记-设计模式-可复用版-Proxy 代理模式


 

https://en.wikipedia.org/wiki/Proxy_pattern


 

如果你发现书中或是线上的文章,讲解并不能够让你很好的理解,交给wikipedia吧,那里会有终极的解释


 

总是喜欢说废话,结构类型的第二个模式选择Proxy的原因是,最近在做的项目要用到PureMVC,更让我惊讶的是,PureMVC竟然进行了更新,我当时使用的版本还是非常老的版本。


 

最近的一次更新是今年的5月份,当时同事跟我说的时候,不太敢相信,跑到github上仔细看了看,而且老版本的一些弊端也在新版本进行了修正,比如发送Command在老版是通过反射实现的,对于频繁的使用,内存和效率都不高,反射本身的速度就是比较慢的,但在新的版本中,这里已经改为使用delegate实现,效果立竿见影。


 

当然Notification消息传递这里还是会出现boxing/unboxing的现象,后面会进行优化,也会再出篇关于PureMVC分析的文章,每一次看相信都会有新的启发。


 

Proxy 代理设计模式


 

概念:

为其它对象,提供一种代理,以控制这个对个访问。


 

这样也避免了我们直接去访问真实对象,从而带来可能的安全隐患。


 

除了代理,也可以称为Wrapper(包装),我们可以执行一些附加的逻辑


 

这时候我想到的第一个现实中的角色就是“代理律师”,为你辩护,什么可以公开,什么不可以,“代理“是非常清楚的,比直接“红果果”的的暴露在外面要”安全“的多。


 

为一个对象进行访问控制的一个原因是:

只有在我们确实需要这个对象的时候,才对它进行创建和初始化。


 

比如有些资源的创建开销很大,比如像网页中的图像,如果我们打开就全部创建,这种开销是非常巨大的,所以通常图片是会先显示一张空白,然后慢慢的下载并显示,这里”空白“的图,就是代理,代替那张真正的图像,直到我们使用的时候,代理去创建真实的对象。


 

这种情况,Proxy通常是保存了图像的extent(尺寸),有了图像尺寸,Proxy就无须真正的实例化就可以响应格式化程序对图像尺寸的要求。


 

虚代理(Virtual Proxy)

我们将上面这种情况,叫做虚代理 (Virtual Proxy) 根据需要创建开销很大的对象。只有在真正需要的时候,我才创建,否则返回的就是代理的对象(一张包仅包含尺寸的空白图)


 

虚代理(Virtual Proxy)应用非常广泛,比如我们打开相册,不可能一下加载所有的图片,

都是会先显示一些图像的基础信息(尺寸),然后动态的增量加载,这样可以避免一次加载,造成卡卡顿和内存紧张的情况


 

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1


 

ImageProxy是Image的代理,只有当我调用Draw的时候,才真正的去加载图像,访问GetExtent,如果真实的对象没有初始化,就返回代理的信息。


 

远程代理(Remote Proxy)


 

为一个对象在不同的地址空间,提供局部的代表。


 

“不同的地址空间”?


 

这里是指远程的数据,可能是在不同的机器上,即不同的地址空间。


 

书上指出NEXTSTEP(Mac系统的基础)中NXProxy就是远程代理。


 

但并没有给出明确的例子说明,大话设计模式上也未能提供,网上找了一些实现,但还是不能很好的理解其含义,所以wiki来了


 

In distributed object communication, a local object represents a remote object (one that belongs to a different address space). The local object is a proxy for the remote object, and method invocation on the local object results in remote method invocation on the remote object. An example would be an ATM implementation, where the ATM might hold proxy objects for bank information that exists in the remote server.


 

remote Proxy通常是指分布式通信场景中,本地对象是远程对象的代理,这个远程对象来自于不同的地址空间


 

我调用本地对象,本地对象是远程对象的代理,实际上是代理负责去调用的远程对象的方法


 

ATM机就是一个RemoteProxy例子,ATM(本地)对象,是当前用户在银行信息的代理


 

还一个我们开发不可或缺的例子:VPN:)(Don't Say Any More)


 

保护代理(Protection Proxy)


 

控制对真实对象的访问,这是最广泛的地方,比如PureMVC中的Proxy,就是典型的Protection Proxy,控制对Model数据的访问,只会暴露部分的接口,并一定程度隐藏其内部的实现。


 

在GOF中,将智能指针也列为代理应用之一,但我并不认为他可以成为一种应用类型,这和保护代理其实没什么区别,一样控制真实对象的访问,也同样可以执行一些其它附加的逻辑,比如记录访问的次数,是否处于调用,锁定状态等等。


 

所以,通常Proxy的应用主要有三种:

1.虚代理(Virtual Proxy)

2.远程代理(Remote Proxy)

3.保护代理(Protection Proxy)


 

结构图:

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1


 

Subject-

定义RealSubject和 Proxy之间的公共接口,这样在使用RealSubject的地方,也可以使用Proxy


 

但这个实际应用中,并不是必须的,要视具体的情况


 

RealSubject-

Proxy代理的真实对象


 

Proxy并不总是需要知道实体的类型,若Proxy完全可以通过一个抽象的接口处理它的实体(真实对象),则无需为每一个RealSubject都生成一个对应的Proxy,Proxy可以统一处理所有的RealSubject,除非Proxy 需要实例化具体的RealSubject(比如Virtual Proxy),那么必须有对应的Proxy.


 

在C#中,可以理解为面向接口,如果通过接口就可以处理RealSubject,那么就不需要知道具体的RealSubject了


 

在wiki上提供了一个Example,正好符合上面的解释,并且可以转换成Virtual Proxy


 

//Subject

interfaceICar

{

    void DriveCar();

}


 

// Real Subject

publicclassCar : ICar

{

    publicvoid DriveCar()

    {

        Debug.Log("Car has been driven!");

     }

}


 

// Proxy

publicclassProxyCar : ICar

{

    privateDriver driver;

    privateICar realCar;


 

    publicProxyCar(Driver driver)

    {

        this.driver = driver;

        this.realCar = newCar();

     }


 

    publicvoid DriveCar()

    {

        if (driver.Age < 16)

            Debug.Log("Sorry, the driver is too young to drive.");

        else

            this.realCar.DriveCar();

     }

}


 

publicclassDriver

{

    publicint Age {  get; set;  }


 

    publicDriver(int age)

    {

        this.Age = age;

     }

}


 

测试代码:

ICar car = newProxyCar(newDriver(15));

        car.DriveCar();


 

        car = newProxyCar(newDriver(25));

        car.DriveCar();


 

例子说明:


 

ICar-

Subject,为Real Subject和Proxy定义共用的接口,这样使用Real Subject的地方,也可以使用Proxy


 

Car-

Real Subject


 

ProxyCar-

是Car的代理


 

但我们可以看到,这个例子中,Proxy完全可以通过一个抽象的接口处理它的实体(真实对象),那么其实没有必要为Car提供一个单独的ProxyCar,比发我加入不同类型的Car,完全可以通过一个Proxy处理。


 

并且,Proxy,并不一定是指代理具体的数据,也可以只是行为部分,因为一个类中包括了数据和行为两部分,这里不要死板的认为(说我自己〜)我代理的,一定是要包含数据的。


 

上面的例子,做下简单的修改,就可以成为Protection Proxy


 

//Subject

interfaceICar

{

    void DriveCar();

}


 


 

// Proxy

publicclassProxyDriver : ICar

{

    privateDriver driver;


 

    publicProxyDriver(Driver driver)

    {

        this.driver = driver;

     }


 

    publicvoid DriveCar()

    {

        if (driver.Age < 16)

            Debug.Log("Sorry, the driver is too young to drive.");

        else

            driver.DriveCar();

     }


 

}


 

//Real Subject

publicclassDriver:ICar

{

    publicint Age {  get; set;  }


 

    publicDriver(int age)

    {

        this.Age = age;

     }


 

    publicvoid DriveCar()

    {

        Debug.Log("Car has been driven!");

     }

}


 

测试代码:

ICar car = newProxyDriver(newDriver(15));

        car.DriveCar();


 

        car = newProxyDriver(newDriver(25));

        car.DriveCar();


 


 

现在Driver是Real Subject,ProxyDriver是Driver的代理


 

最后再简单讨论一下PureMVC中Proxy的作用,以结束Proxy设计模式应用的讲解~


 

640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1


 


 

Model是指我们游戏中各种数据,比如角色,任务,成就,技能等等,在PureMVC中,我们不可以直接与这些数据进行交互, Model是通过Proxy进行保护访问(Protection Proxy),所有Model的交互处理,均是通过Proxy处理,

可以看到并不是一个Model对应一个Proxy,比如角色的数据,包含了当前完成的任务,还有成就等等,他们是可以放在一个Proxy中处理的,但是属于不同的Model.


 

最近项目中在使用PureMVC,后面会再推出相关的经验分享,Proxy 代理设计模式分享到此


 

                     Coffee Factory 新奥购物中心

                     2019.6.16

  • 允许他人重新传播作品,但他人重新传播时必须在所使用作品的正文开头的显著位置,注明用户的姓名、来源及其采用的知识共享协议,并与该作品在磨坊上的原发地址建立链接
  • 可对作品重新编排、修改、节选或者以作品为基础进行创作和发布
  • 可将作品进行商业性使用

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引