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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容