设计模式:代理模式

以下是从java设计模式刘伟一书总结:
代理模式是常用的结构型设计模式之一,当无法直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,所访问的真实对象与代理对象需要实现相同的接口。根据代理模式的使用目的不同,代理模式又可以分为多种类型,例如保护代理、远程代理、虚拟代理、缓冲代理等,它们应用于不同的场合,满足用户
的不同需求。
代理模式定义如下:

代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。
Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.
代理模式是一种对象结构型模式。在代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新服务。
1.代理模式结构:

在这里插入图片描述

2.代理模式包含如下三个角色:

(1) Subject(抽象主题角色):它声明了真实主题和代理主题的共同接口,这样一来在任何使
用真实主题的地方都可以使用代理主题,客户端通常需要针对抽象主题角色进行编程。
(2) Proxy(代理主题角色):它包含了对真实主题的引用,从而可以在任何时候操作真实主题
对象;在代理主题角色中提供一个与真实主题角色相同的接口,以便在任何时候都可以替代
真实主题;代理主题角色还可以控制对真实主题的使用,负责在需要的时候创建和删除真实
主题对象,并对真实主题对象的使用加以约束。通常,在代理主题角色中,客户端在调用所
引用的真实主题操作之前或之后还需要执行其他操作,而不仅仅是单纯调用真实主题对象中
的操作。
(3) RealSubject(真实主题角色):它定义了代理角色所代表的真实对象,在真实主题角色中
实现了真实的业务操作,客户端可以通过代理主题角色间接调用真实主题角色中定义的操作。

3.代理模式根据其目的和实现方式不同可分为很多种类,其中常用的几种代理模式简要说明如下:

(1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这
个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使
(Ambassador)。

(2) 虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较
小的对象来表示,真实对象只在需要时才会被真正创建。

(3) 保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

(4) 缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端
可以共享这些结果。

(5) 智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如
将对象被调用的次数记录下来等。

4.实例展示:
下面进行一个信息咨询公司的收费商务信息查询系统的开发设计,该系统的基本需求
如下:

(1) 在进行商务信息查询之前用户需要通过身份验证,只有合法用户才能够使用该查询系统;
(2) 在进行商务信息查询时系统需要记录查询日志,以便根据查询次数收取查询费用。该软件公司开发人员已完成了商务信息查询模块的开发任务,现希望能够以一种松耦合的方式向原有系统增加身份验证和日志记录功能,客户端代码可以无区别地对待原始的商务信息查询模块和增加新功能之后的商务信息查询模块,而且可能在将来还要在该信息查询模块中增加一些新的功能。

实例分析及类图:
通过分析,可以采用一种间接访问的方式来实现该商务信息查询系统的设计,在客户端对象
和信息查询对象之间增加一个代理对象,让代理对象来实现身份验证和日志记录等功能,而
无须直接对原有的商务信息查询对象进行修改,如图:
在这里插入图片描述
在这里插入图片描述
业务类AccessValidator用于验证用户身份,业务类Logger用于记录用户查询日志,Searcher充当抽象主题角色,RealSearcher充当真实主题角色,ProxySearcher充当代理主题角色。

代码实现:

//身份验证类,业务类,它提供方法Validate()来实现身份验证
class AccessValidator{
    //模拟实现登录验证
    public boolean Validate(String userId)
    {
        System.out.println("在数据库中验证用户'" + userId + "'是否是合法用户?");
        if (userId.equals("杨过")) {
            System.out.println("'{0}'登录成功!"+userId);
            return true;
        }
        else {
            System.out.println("'{0}'登录失败!"+userId);
            return false;
        }
    }
}
//日志记录类,业务类,它提供方法Log()来保存日志。
class Logger
{
    //模拟实现日志记录
    public void Log(String userId) {
        System.out.println("更新数据库,用户'{0}'查询次数加1!"+userId);
    }
}
//抽象查询类,充当抽象主题角色,它声明了DoSearch()方法。
interface Searcher
{
    String DoSearch(String userId, String keyword);
}

//具体查询类,充当真实主题角色,它实现查询功能,提供方法DoSearch()来查询信息。
class RealSearcher implements Searcher {
//模拟查询商务信息
    public String DoSearch(String userId, String keyword) {
        System.out.println("用户'{0}'使用关键词'{1}'查询商务信息!"+userId+keyword);
        return "返回具体内容";
        }
}


//代理查询类,充当代理主题角色,它是查询代理,维持了对RealSearcher对
//象、AccessValidator对象和Logger对象的引用。
class ProxySearcher implements Searcher {
    private RealSearcher searcher = new RealSearcher(); //维持一个对真实主题的引用
    private AccessValidator validator;
    private Logger logger;

    public String DoSearch(String userId, String keyword) {
//如果身份验证成功,则执行查询
        if (this.Validate(userId)) {
            String result = searcher.DoSearch(userId, keyword); //调用真实主this.Log(userId); //记录查询日志
            return result; //返回查询结果
        } else {
            return null;
        }
    }

    //创建访问验证对象并调用其Validate()方法实现身份验证
    public boolean Validate(String userId) {
        validator = new AccessValidator();
        return validator.Validate(userId);
    }

    //创建日志记录对象并调用其Log()方法实现日志记录
    public void Log(String userId) {
        logger = new Logger();
        logger.Log(userId);
    }
}
//客户端测试类
class Program
{
    public static void main(String[] args) {
        //读取配置文件
        try {
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            Document document = documentBuilder.parse(new File("config.xml"));
           //获取包含图表类型的文本节点
            String proxy = document.getElementsByTagName("proxy").item(0).getFirstChild().getNodeValue().trim();
            //通过反射获取代理类对象
            Class<?> aClass = Class.forName(proxy);
            //无论是哪个代理类对象都转成接口对象
            Searcher searcher = (Searcher)aClass.newInstance();
            String doSearch = searcher.DoSearch("杨过", "九阴真经");
            System.out.println(doSearch);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

配置文件config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <proxy>com.zzy.design.ProxySearcher</proxy>
</config>

output:
在这里插入图片描述
代理模式的共同优点如下:
(1) 能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。
(2) 客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原
则,系统具有较好的灵活性和可扩展性。此外,不同类型的代理模式也具有独特的优点,例如:
(1) 远程代理为位于两个不同地址空间对象的访问提供了一种实现机制,可以将一些消耗资源
较多的对象和操作移至性能更好的计算机上,提高系统的整体运行效率。
(2) 虚拟代理通过一个消耗资源较少的对象来代表一个消耗资源较多的对象,可以在一定程度
上节省系统的运行开销。
(3) 缓冲代理为某一个操作的结果提供临时的缓存存储空间,以便在后续使用中能够共享这些
结果,优化系统性能,缩短执行时间。
(4) 保护代理可以控制对一个对象的访问权限,为不同用户提供不同级别的使用权限。

代理模式的主要缺点如下:
(1) 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求
的处理速度变慢,例如保护代理。
(2) 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。

代理模式的主要缺点如下:
(1) 由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求
的处理速度变慢,例如保护代理。
(2) 实现代理模式需要额外的工作,而且有些代理模式的实现过程较为复杂,例如远程代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值