设计模式学习笔记09-Proxy(代理)模式

本文主要是看了《设计模式》做的笔记和思考,在此分享仅代表个人观点,如有不对的地方欢迎批评和指正。

基础

Proxy模式,中文名“代理模式”,该模式的主要想法是用来管理被代理的类,在访问真正的类之前,你只能访问到一个代理类,代理类会根据需要合理地支配被代理类,UML如下所示。


Proxy模式UML.png

此处,代理类和被代理类都应当实现同样的接口,以方便实现一致性,使得代理类和被代理类的使用过程相同。

代理模式的四种使用情况

  1. 远程代理
  2. 虚代理
  3. 保护代理
  4. 智能指引

下面将逐个讲讲其概念以及给出简单的代码作为演示。

远程代理

Remote Proxy可以隐藏一个对象存在与不同地址空间的事实。
为了达到Remote Proxy的描述,我的理解有以下两种情况:

  1. 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();
    }
}
  1. Proxy类充当一个负载均衡器的作用,在内存的不同地方有多个RealSubject,Proxy类根据实际情况提供某个地址的指针
class RealSubject extends Subject{}

class Proxy extends Subject{
    private ArrayList 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的关系是可以静态表达的、可预见的,也因为这一点,它不够灵活。因此,推荐从设计思想出发,在尽可能地表达业务中各个角色的关系的同时,追求灵活与稳定。

你可能感兴趣的:(设计模式学习笔记09-Proxy(代理)模式)