你不想干我帮你——代理模式

关键字:设计模式,代理模式,proxy,保护代理,虚拟代理,远程代理,缓冲代理,智能引用代理

代理模式

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。

说白了,就是当你不能直接访问一个对象时,通过一个代理对象来间接访问,这种方式就叫做代理模式。

应用场景

代理模式是一种比较常见的结构型设计模式,按照不同的场景,又可以分为保护代理,远程代理,虚拟代理,缓冲代理和智能引用代理。

  • 保护代理,就是为原对象设置不同的访问权限,处于被保护的状态,不可直接访问,用户在访问时根据自己不同的权限来访问。
  • 远程代理,在本地创建一个远程对象的本地代理,通过访问该本地代理实现访问远程对象的目的,也叫大使(Ambassador)
  • 虚拟代理,虚拟的意思是假的,当要创建一个复杂巨大的对象时,先创建一个小的代理对象,当具体使用时再创建真实对象,有点“懒加载”的意思。
  • 缓冲代理,为针对原对象的某种操作提供一个临时的缓冲代理,用来分担访问原对象的压力,以便多客户端快速共享这些资源。
  • 智能引用代理,相当于对原对象的一种功能扩展,在访问原对象时,加入了新功能,例如统计访问次数等。

UML类图分析

通过上面的定义介绍,我想我们对代理模式已经有了初步的认识,心中已经有了一种架构图的出现:

Client无法直接访问Real,这时出现了一个Proxy类继承自Real,Client可以直接访问Proxy来使用Real的资源。看上去是这样,但是Proxy和Real不应该是继承的关系。

为什么Proxy不能简单的继承Real来达到代理模式的设计?

来看一下上面介绍过的代理模式的应用场景,我们希望通过代理模式的架构来给原对象创建一个代理类Proxy,它可以为原对象提供不同的访问权限,可以继承一部分原对象开放出来的接口,可以为原对象增添新功能,可以只是一个原对象的创建方法,作为原对象的一个过度,而这些,不能局限于一个基于强连接的直接继承关系。下面我用一段代码来说明Proxy不能简单继承Real的原因

package pattern.proxy;

public class Real {
    private long id;
    private String password;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

}
package pattern.proxy;

public class Proxy extends Real {

}
package pattern.proxy;

import org.junit.Test;

public class Client {
    private Proxy proxy = null;

    @Test
    public void testSimpleProxy() {
        proxy = new Proxy();
        proxy.getId();
        proxy.getPassword();// 问题出现了
    }
}

发现了没有,如果是保护代理的话,我们不希望Real类中的password属性被透明访问,只有拥有该权限的客户端才可以访问,其他都无法访问password字段内容。所以,我们需要一个更加灵活的Proxy和Real之间的关系,这个关系一定不是强继承的关系,经过这一番思考,我们发现了代理模式中的核心类:抽象代理类

代理模式的角色

现在来区分代理模式的角色:

  • 抽象代理类,推荐使用抽象类的方式,声明了Real和Proxy的共同接口,这样用来在任何使用Real的地方都可以使用Proxy。客户端要针对该抽象代理类进行编程。
  • 具体代理类,根据合成复用原则,它包含了Real的引用,从而可以在任何时候操作Real对象,同时它与Real都是继承自抽象代理类,并且与Real拥有相同的接口,可以随时替换Real。然后,它可以实现对Real的控制,可以编程创建和删除Real对象,通常来讲,Client通过具体代理类访问Real的接口时,要在具体代理类中预先执行多个补充方法,然后再对Real进行操作。
  • 真实类,Real类,Client不能或者不想直接访问的类。它的对应于Proxy那个共同的接口有着真实的业务操作。

下面来看代码:

Real类改为继承Proxy抽象类,其他不变,Proxy抽象类改为

package pattern.proxy;

public abstract class Proxy {
    public abstract long getId();

    public abstract String getPassword();
}

增加一个同样继承于Proxy类的具体实现类,这里通过与Real建立组合关系将Real的对象作为SuperProxy的成员属性,为了满足多线程要求,这里采用了饿汉单例模式

package pattern.proxy;

public class SuperProxy extends Proxy {
    private static Real real = new Real();// 直接采用饿汉单例

    /**
     * 要想操作Real,要先执行具体Proxy类中的一些其他方法,或许是创建Real对象,也或许是准备数据。
     */

    static {
        real.setId(12312l);
        real.setPassword("dontknow");
    }

    @Override
    public long getId() {
        return real.getId();
    }

    @Override
    public String getPassword() {
        return real.getPassword();
    }

}

客户端调用方法:

package pattern.proxy;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

public class Client {
    private final static Logger logger = LogManager.getLogger();
    private Proxy proxy = null;

    @Test
    public void testSimpleProxy() {
        proxy = new SuperProxy();
        long id = proxy.getId();
        String pwd = proxy.getPassword();
        logger.info("id:" + id + " password:" + pwd);
    }
}

输出:20:35:10[testSimpleProxy][main]: id:12312 password:dontknow

业务实例(保护代理和智能引用代理)

业务需求:Client在访问Real的时候,要先进行身份验证并且记录访问次数。而同时,不可对Real内部进行修改。

这种情况下,我们可以通过代理模式增加代理类来实现,下面看代码:

首先定义一个代理抽象基类,加入共有抽象方法(此方法一定是已存在与RealSearcher的,因为RealSearcher内部不可改变)

package pattern.proxy.search;

public abstract class Searcher {
    public abstract String doSearch(String username, int sid);
}

将原RealSearcher改为继承于代理代理抽象基类,其他不必做任何修改。

package pattern.proxy.search;

import java.util.HashMap;
import java.util.Map;

public class RealSearcher extends Searcher {
    private Map<Integer, String> data = new HashMap<Integer, String>();

    RealSearcher() {// 模仿数据源,对象构造时初始化数据
        data.put(1001, "fridge");
        data.put(1002, "book");
        data.put(1003, "macrowave oven");
    }

    public String doSearch(String username, int sid) {
        return data.get(sid);
    }
}

此时,创建一个新的具体代理类,加入用户校验和访问次数功能。

package pattern.proxy.search;

public class ProxySearcher extends Searcher {

    private Searcher searcher = new RealSearcher();

    private int count;

    public String doSearch(String username, int sid) {
        if (validateUser(username)) {
            count++;
            return "times: " + count + " " + searcher.doSearch(username, sid);
        }
        return "Identity discrepancy";
    }

    private boolean validateUser(String username) {
        if ("jhon".equals(username))
            return true;
        return false;
    }
}

最后是客户端调用的方式,因为具体代理类和真实对象都是继承于代理抽象基类,因此可以创建抽象基类的不同子类的实例,同时他们都拥有原属于真实对象的查询方法。

package pattern.proxy.search;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;

public class Client {
    private final static Logger logger = LogManager.getLogger();

    @Test
    public void testSearcher() {
        Searcher searcher = new ProxySearcher();// 创建一个代理类对象,而不是RealSearcher
        logger.info(searcher.doSearch("jhon", 1002));
        logger.info(searcher.doSearch("jhon", 1001));
        logger.info(searcher.doSearch("jack", 1002));
        logger.info(searcher.doSearch("java", 1003));
    }
}

输出结果:

11:06:03[testSearcher][main]: times: 1 book
11:06:03[testSearcher][main]: times: 2 fridge
11:06:03[testSearcher][main]: Identity discrepancy
11:06:03[testSearcher][main]: Identity discrepancy

通过结果可以看出,我们只改变了真实查找类的继承关系即可实现增加用户验证和访问次数的功能。

其他代理场景

我们所有的代理模型都可以采用上面给出的类图结构,上面的代码实例介绍了保护代理和智能引用代理。下面再介绍一下其他的代理场景。

  • 远程代理,上面简单介绍了一下,但其实远程代理在实际工作中有着广泛的应用,因为我们的程序往往需要远程主机的支持,或许是因为远程主机有着自己的服务接口,也或许远程主机有更加高效的环境,而远程代理可以将本地与远程主机直接的网络通信相关的操作封装起来,让我们本地的程序可以直接使用远程主机的好的性能或者功能。这部分由于模仿起来不好实现,因此如果未来在研究源码时遇到远程代理相关的内容会再详细介绍它在代码中具体的运用。
  • 虚拟代理同样有着广泛的使用。在异步通信中,它可以显著解决由于对象本身复杂性,消耗大量系统资源或者网络原因造成的对象加载时间过久的问题,这时候可以通过多线程技术,使一个线程加载一个速度较快的小对象来代替原对象承接客户端新的操作,其他线程则继续加载原对象。
  • 缓冲代理也是非常实用的模型,它可以建立一个临时内存空间存储那些操作较频繁的结果,使得更多的客户端可以直接共享这些结果而不必再去重复查找。其实我们在上面的代码中也无意间使用到了缓冲代理,只是没完全按照代理模式的架构去写,那就是RealSearcher的数据初始化部分,其实实际工作中数据都是来自于真实的数据源,例如数据库,或者网络通信,而我们这里相当于直接将数据源中的数据缓存在了本地程序中,当jvm启动的时候会在内存中创建这些数据,而jvm终止以后,这些数据也就没了,通过缓冲代理,客户端可以获得更快的访问速度,同时也减少了对真实数据源的访问次数。

代理模式总结

代理模式是非常常用的结构型设计模式,尤其是我们前面介绍过的保护代理,远程代理,虚拟代理,缓冲代理以及智能引用代理,本文介绍了他们的主旨思想,给出了代理模式的核心架构,解释了代理模式的原理,未来在其他内容的研究过程中,会碰到真实场景中的代理模式的应用,我会深入介绍。

时间: 2024-10-17 19:16:32

你不想干我帮你——代理模式的相关文章

JAVA CAS单点登录之三:CAS代理模式演练

前言 JAVA CAS单点登录之一:搭建CAS服务器 JAVA CAS单点登录之二:CAS普通模式1演练 代理模式相相对上一节的普通模式,更加复杂了.但配置起来也会稍微有些差别.所谓难者不会,会者不难.如果遇到一个从来没有遇到的问题,解决起来也是非常棘手的,当然解决之后就不是事了.我就遇到了一个CAS 坑爹的错误.一步步按照别人的博客坐下来,普通模式部署没有多大问题,就是不知道为什么代理模式总是出错,搜遍了整个网络,也没找到问题所在,我就纳闷了,为什么就没有人 遇到过呢.还好,最后我使用了杀手锏

代理模式——公司的代言人

静态代理 随着业务规模的增大,为了方便管理两间工厂,小成和他的合伙人建立了一间公司,把一些不是很重要的生意交给手下业务员代表公司去和其他公司谈,如果业务员超常发挥,还可能为公司谈好一笔任务之外的生意.这样老板小成就可以轻松很多了,小成一有空就想写代码,一想这个不就是代理模式吗,然后就开始写下代码. 介绍 在有些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个代理来实现间接引用.就像我们现在的不能直接访问谷歌,要通过代理翻墙才行. 代理模式的定义就是为其他对象提供一种代理以控制对这个

代理模式:女朋友这么漂亮,你缺经纪人吗?

你缺经纪人吗 这几天王宝强妻子出轨经纪人事件惹尽了眼球,"你缺经纪人吗" 一下子成为打招呼必备热词.在紧跟"潮流"之前我们有必要了解下,经纪人都负责为宝强做哪些事呢? 为宝强安排档期 为宝强做公关 为宝强照顾家庭 可以看到,除了吃喝拉撒睡,宝强的许多行为都被经纪人 架空 了: Created with Rapha?l 2.1.0外界外界宋吉吉宋吉吉王宝强王宝强宝强接不接"跑男"你还是去"真男",给我俩点二人世界代理架空宝强妻子

iOS中的自定义代理模式

iOS中的自定义代理模式 自定义代理模式分为6步,遵循这6步,就能把代理完整的实现. 1.定义协议(协议中存放代理的任务).我们对于有的自定义的布局视图,并不会带有协议,为了满足我们有时候的开发需求,需要添加代理.那么我们就要先定义一个协议.如下:在自定义视图的DelegateView.h文件中定义一个DelegateViewDelegate协议,协议默认的都是必须的实现的方法,可以根据需求来添加可选实现的方法. 1.定义协议 2.定义代理属性,用来存储代理对象. 2.定义代理属性 3.为Del

适配器 蒙面模式 代理模式

这次我把适配器,代理模式,蒙面模式敲了,终于理解了这是怎么回事.C++的设计模式实际上就是软件的功能架构,她想办法处理好类与类之间的关系,让软件好用,易操作,方便修改.软件的所遵循的原则是开闭原则,意思就是开放接口,关闭修改,我们可以去扩展:说到扩展就要提及到这六个字"高内聚,低耦合",我们尽可能的要将功能单一化,一个模块写一个功能,那么我们需要改哪一个功能只需要找到相应就可以,这样做也容易扩展,我增一个功能进来也很easy,举个例子来说吧,假如说一个汽车,我们把汽车可以分为多个模块,

《Java设计模式》之代理模式

1,什么是代理模式? 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问. 2,策略模式有什么好处? 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 3,代理模式一般涉及到的角色有: 抽象角色:声明真实对象和代理对象的共同接口: 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象.同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当

JDK动态代理[1]----代理模式实现方式的概要介绍

日常工作中经常会接触到代理模式,但一直没有对其进行深究.代理模式一直就像一团迷雾一样存在我心里,什么是代理模式?为什么要使用代理?代理模式有哪些实现?它的底层机制是怎样的?这些问题促使着我迫切想要揭开代理模式的神秘面纱. 1. 什么是代理模式? 日常生活中我们经常会碰到代理模式,例如我们找房产中介帮我们介绍房子,找婚姻中介帮我们介绍对象,找保洁帮我们打理房间,找律师帮我们进行诉讼等.我们在无形中运用到了代理模式,却不知道它的存在. 2. 为什么要使用代理? 运用代理可以使我们的生活更加便利,有了

设计模式漫谈之代理模式

设计模式这个东西理论上多,说的人多,能理解的人少,愿意看的人少,因此我这个浏览量上不去很正常.不过我希望帮助到真正想看的人.刚和以前同事通了电话,程序员同事之间如果想成为朋友,必须一起拼搏,等若干年后,大家回忆起来那段经历,自然友谊常在!今天去见了同事给我介绍的女朋友,小孩无娘,说起来话长,一句话,我真的需要找一个好姑娘谈恋爱结婚了. 对象与对象之间若有关系,需要一个对象能访问到另一个对象的引用.编译型语言,需要先设计这种关系(设计类),然后再复制对象.老人有言"露头椽子先糟",在国内

Java进阶篇设计模式之七 ----- 享元模式和代理模式

前言 在上一篇中我们学习了结构型模式的组合模式和过滤器模式.本篇则来学习下结构型模式最后的两个模式, 享元模式和代理模式. 享元模式 简介 享元模式主要用于减少创建对象的数量,以减少内存占用和提高性能.这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式. 用通俗的话来说就是进行共用.生活中也有一些例子,比如之前很火的共享单车,更早之前的图书馆,编程中经常用的String类,数据库连接池等等.当然,享元模式主要的目的是复用,如果该对象没有的话,就会进行创建. 享