本文主要是看了《设计模式》做的笔记和思考,在此分享仅代表个人观点,如有不对的地方欢迎批评和指正。
基础
Proxy模式,中文名“代理模式”,该模式的主要想法是用来管理被代理的类,在访问真正的类之前,你只能访问到一个代理类,代理类会根据需要合理地支配被代理类,UML如下所示。
此处,代理类和被代理类都应当实现同样的接口,以方便实现一致性,使得代理类和被代理类的使用过程相同。
代理模式的四种使用情况
- 远程代理
- 虚代理
- 保护代理
- 智能指引
下面将逐个讲讲其概念以及给出简单的代码作为演示。
远程代理
Remote Proxy可以隐藏一个对象存在与不同地址空间的事实。
为了达到Remote Proxy的描述,我的理解有以下两种情况:
- Proxy类中有多个RealSubject的指针,这些不同的RealSubject共同完成了一个功能,而Proxy类则向外提供了一个整体的入口,有点类似Facade(外观)模式,代码示例如下:
class A extends Subject{}
class B extends Subject{}
class Proxy extends Subject{
private A a;
private B b;
public doSomething(){
a.doSomething();
b.doSomething();
}
}
- Proxy类充当一个负载均衡器的作用,在内存的不同地方有多个RealSubject,Proxy类根据实际情况提供某个地址的指针
class RealSubject extends Subject{}
class Proxy extends Subject{
private ArrayList<RealSubject> list;
private static int counter = 0;
public doSomething(){
list.get(counter%list.size()).doSomething();
counter++;
}
}
虚代理
如果RealSubject在执行某种操作时开销很大,可以考虑使用Proxy类缓存这些操作的结果,当操作的输入不变时,直接拿Proxy类缓存的结果返回,节省开销。又或者是RealSubject本身在创建时就消耗很多资源,但有些功能并不需要完整的RealSubject类执行,那么这些小功能就可以由Proxy类代劳,到实在需要的时候再实例化RealSubject。示例代码如下所示。
abstract class Image{
public double getSize();
public String getName();
public void draw();
}
class RealImage extends Image{
private String name;
private int status;
// 构造方法,注意它与Proxy中的不同
public RealImage(String name){
this.name = name;
...
}
public double getSize(){、
// 经过某些特殊处理...
}
public String getName(){...}
public void draw(){
// big project ...
}
// 返回一个状态标识
public int getStatusFlag(){...}
}
class ImageProxy extends Image{
private RealImage image;
private int imageStatus = 0; //表示初始状态
private double imageSize = 0;
private String imageName = "";
public ImageProxy(String name){
imageName = name;
image = "";
}
public double getSize(){
if(image == null){
image = new RealImage();
}
// 如果RealImage的状态没有改变,那么直接返回缓存的值
if(imageStatus != image.getStatusFlag()){
imageSize = image.getSize();
}
return imageSize;
}
// 这个方法就不经过RealImage
public String getName(){
return imageName;
}
// 这时不用RealImage实在不行了
public void draw(){
if(image == null){
image = new RealImage();
}
image.draw();
}
}
使用时就直接使用ImageProxy就好。
保护代理
这个就是我在外面看的好几个博客讲的情形,大致描述一下:代理类会审核发给被代理类的请求。例子跟这个类似:A找B玩,结果B妈来见A,B妈说B要读书学习,在A看来,这跟B自己说要学习结果一样。这个例子中,B妈就是B的代理,在A提出玩的请求时她先把关一下,符合条件了再把A的请求转发给B。
智能指针
这里的智能主要描述的是内存管理,包括内存回收、长期持有一个对象、同步锁检测。
要做到内存回收,那么通常需要记下引用计数,当计数降到一个阈值后触发内存回收,以下代码同时还展示了同步锁的使用。
abstract class Box{
public void putSomething(Object o);
public void takeSomething(Object o);
}
// 省略被代理类RealBox
// 假设因为某种原因,RealBox中并没有计数,也没有同步锁
class ProxyBox extends Subject{
private int thingsQty = 0;
private RealBox box; // ProxyBox将在构造方法中创建
private bool isReleaseBox = false;
public void synchronize putSomething(Object o){
if(isReleaseBox){
System.out.println("这个盒子已空");
return;
}
thingsQty++;
box.putSomething(o);
}
public void synchronize takeSomething(Object o){
if(isReleaseBox){
System.out.println("这个盒子已空");
return;
}
if(thingsQty == 0){
System.out.println("这个盒子已空");
box = null;
isReleaseBox = true;
}else{
box.takeSomething(o);
}
}
}
main(){
Box box = new ProxyBox();
box.putSomething("apple");
box.takeSomething("apple");
}
我觉得这种计数在RealSubject类本身其实就能做,除非语言不支持在类中释放自己。当然还有像上面例子中的情况可以使用,就是当你的队友出现了疏忽,你又不愿意改他的类,那么可以通过写一个Proxy类绕过这些坑。此时,这个Proxy已经跟Decorator很像了,只要去除Proxy的权限,这个就是Decorator模式,此处是根据isReleaseBox设置权限,如果读者有更好的方法欢迎提出。
Proxy(代理)模式与Decorator(装饰)模式
需要说明的是,在Decorator只有一层的时候,这两个模式连UML都极为相似,如上文所说,一旦Proxy类放弃对RealSubject的控制,它很大概率就是个Decorator类。但从设计思想来说,它们区别挺大的。Decorator模式主要适用于应对不可预见的功能(设计者甚至不知道用户会拿它的类嵌套实现什么功能),因此它非常的灵活,也能够递归地、层级式地完成某些功能。Proxy模式主要适用于不便于访问RealSubject时使用,因而,Proxy类与RealSubject的关系是可以静态表达的、可预见的,也因为这一点,它不够灵活。因此,推荐从设计思想出发,在尽可能地表达业务中各个角色的关系的同时,追求灵活与稳定。