灵巧守门员——代理模式(Java实现)

news2024/9/21 19:08:10

代理模式不仅在控制对象访问上展现了其独特的优势,还在功能扩展和系统安全性上起到了重要作用。上期我们介绍了代理模式在Python中的实现,今天,我们将继续探讨代理模式,并展示如何在Java中实现它。

什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供了一种代理以控制对这些对象的访问;可以把代理模式理解为一个“守门员”,在客户(Client)和真实对象(RealSubject)之间充当中介,控制请求的传递和处理—— 就像你想看一部付费电影时,需要先通过一个“守门员”确认你是否支付了费用,这名守门员就是代理,他会在你访问电影资源之前先做一些检查,确保你有权利观看。

UML图

示意图

代码实现

接下来,我们通过代码展示如何在Java中实现一些常用的代理模式——静态代理、动态代理、保护代理、虚拟代理。

静态代理实现
// 定义接口
interface Subject {
    void request();
}

// 真实对象类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实对象:处理请求。");
    }
}

// 代理类
class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("代理:在真实对象之前检查权限。");
        realSubject.request();
        System.out.println("代理:在真实对象之后记录日志。");
    }
}

// 使用代理
public class StaticProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Proxy proxy = new Proxy(realSubject);
        proxy.request();
    }
}

代码解析: 在这个例子中,Proxy 类通过组合的方式持有 RealSubject 的实例,并在 request 方法的执行前后分别加入了权限检查和日志记录功能,静态代理的代理行为在编译时确定,适合结构固定的场景。

动态代理实现

Java中的动态代理通过java.lang.reflect.Proxy类和InvocationHandler接口来实现,它可以在运行时动态生成代理类:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface Subject {
    void request();
}

// 真实对象类
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("真实对象:处理请求。");
    }
}

// 动态代理处理器
class DynamicProxyHandler implements InvocationHandler {
    private Object realObject;

    public DynamicProxyHandler(Object realObject) {
        this.realObject = realObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理:在真实对象之前执行操作。");
        Object result = method.invoke(realObject, args);
        System.out.println("代理:在真实对象之后执行操作。");
        return result;
    }
}

// 使用动态代理
public class DynamicProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxyInstance = (Subject) Proxy.newProxyInstance(
            realSubject.getClass().getClassLoader(),
            realSubject.getClass().getInterfaces(),
            new DynamicProxyHandler(realSubject)
        );

        proxyInstance.request();
    }
}

代码解析: 在这个例子中,动态代理通过 InvocationHandler 来拦截方法调用,并在调用前后加入了自定义的处理逻辑,动态代理允许我们在运行时为任意接口生成代理对象,非常适合需要灵活扩展的场景。

保护代理实现

保护代理用于控制对对象的访问权限,以下是一个简单的保护代理实现:

// 定义接口
interface Subject {
    void request(String userRole);
}

// 真实对象类
class RealSubject implements Subject {
    @Override
    public void request(String userRole) {
        System.out.println(userRole + " 访问:执行敏感操作。");
    }
}

// 保护代理类
class ProtectionProxy implements Subject {
    private RealSubject realSubject;

    public ProtectionProxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request(String userRole) {
        if ("Admin".equals(userRole)) {
            realSubject.request(userRole);
        } else {
            System.out.println("访问被拒绝:" + userRole + " 没有权限。");
        }
    }
}

// 使用保护代理
public class ProtectionProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProtectionProxy proxy = new ProtectionProxy(realSubject);

        proxy.request("Admin"); // 输出: Admin 访问:执行敏感操作。
        proxy.request("Guest"); // 输出: 访问被拒绝:Guest 没有权限。
    }
}

代码解析: 在这个保护代理的例子中,ProtectionProxy 类根据用户的角色决定是否允许访问真实对象,只有管理员(Admin)角色的用户才能执行操作,其他角色则被拒绝访问,这种模式非常适用于权限控制系统。

虚拟代理实现

虚拟代理用于延迟加载昂贵的资源,只有在真正需要时才创建对象:

// 定义接口
interface Resource {
    void load();
}

// 重型资源类
class HeavyResource implements Resource {
    @Override
    public void load() {
        System.out.println("加载大量数据...");
    }
}

// 虚拟代理类
class VirtualProxy implements Resource {
    private HeavyResource realResource;

    @Override
    public void load() {
        if (realResource == null) {
            realResource = new HeavyResource();
            realResource.load();
        }
        System.out.println("资源已加载,执行操作。");
    }
}

// 使用虚拟代理
public class VirtualProxyDemo {
    public static void main(String[] args) {
        Resource proxy = new VirtualProxy();
        proxy.load(); // 首次调用会加载数据
        proxy.load(); // 第二次调用不会再次加载数据
    }
}

代码解析: 在这个虚拟代理的例子中,VirtualProxy 控制了对 HeavyResource 的访问,只有在第一次调用 load 方法时才会加载实际资源,后续调用不会再次加载,这种模式非常适合需要优化性能的场景。

适用场景

代理模式的应用非常广泛,以下是一些典型的场景:

  • 远程代理(Remote Proxy):在分布式系统中,客户端通过代理与远程服务器交互,隐藏底层的复杂网络通信;
  • 虚拟代理(Virtual Proxy):当创建昂贵对象时,虚拟代理可以延迟对象的创建,直到真正需要时再进行实例化,如大型图片的延迟加载;
  • 保护代理(Protection Proxy):在权限控制系统中,通过代理来控制对敏感数据或操作的访问权限,不同用户的权限判断就是一个典型应用;
  • 缓存代理(Caching Proxy):代理模式可以缓存频繁访问的结果,减少系统负载并提升响应速度,例如缓存数据库查询结果。

优缺点

优点:

  • 功能增强:代理模式允许你在不修改原始对象的情况下,添加新的功能;
  • 访问控制:代理模式可以帮助控制对原始对象的访问权限,提升系统的安全性和稳定性。

缺点:

  • 增加复杂性:引入代理模式后,系统的复杂性会增加,尤其是在多个代理叠加使用时;
  • 性能开销:代理模式可能会带来额外的性能开销,特别是在频繁调用的场景中,这一点需要特别注意。

总结

通过这篇文章,希望读者能够更好地理解代理模式在Java中的实现,并能够在实际开发中灵活应用这种设计模式。如果你有任何疑问或想法,欢迎在下方留言!别忘了关注我们的公众号,获取更多有趣的编程知识和实用的代码技巧,我们期待与你的交流与分享!
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2038034.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

How do you implement OpenAI GPT-3 Api Client in PHP?

题意:如何在 PHP 中实现 OpenAI GPT-3 API 客户端? 问题背景: I need help understanding the vague instructions on https://packagist.org/packages/orhanerday/open-ai 我需要帮助来理解这些模糊的说明... I downloaded the package fr…

PDF转图片新潮流,4款神器告别手动截图

在这个信息爆炸的时代,PDF文件因为能在各种设备上保持格式不变,成了我们学习和工作中的好帮手。今天,我就诚心诚意地给你推荐几款现在特别流行的PDF转图片工具。这些工具操作起来非常简单,转换速度快,而且转换出来的图…

前端工程化工具——plop的使用-生成模板页面

背景 软件列表 软件名称版本号备注vue3.2.13vue-cli5.0.8plop4.0.1 操作步骤 新建vue3工程 vue create test-plop按提示完成操作即可 modules安装工具选择&#xff1a;yarn或npm均可 安装plop模块 yarn add plop -g新建模板文件 page1/index.hbs <template><…

【启动centos报错】另一个程序已锁定文件的一部分,进程无法访问,打不开磁盘.

启动centos报错 另一个程序已锁定文件的一部分&#xff0c;进程无法访问打不开磁盘“D:\Program2\CentOS\CentOS7\CentOS7.vmdk”或它所依赖的某个快照磁盘。模块“Disk”启动失败。未能启动虚拟机。解决方法 删除.lck文件

基于InP的通用光子集成技术(四)

7.ASPIC设计环境 在光子学中引入通用晶圆方法将导致光子芯片复杂度的重大变化&#xff1a;它将把设计从器件级转移到光路级&#xff0c;这一转变发生在20世纪70年代和80年代的微电子中&#xff0c;现在也发生在PIC中。 在通用方法中&#xff0c;可以通过具有许多构建块的PDK访…

数学建模笔记(2):Topsis分析法

一.评价类问题的几个关键词 1.指标 评价一个方案或者对象我们需要有评价指标&#xff0c;即从哪几个方面进行评价。比如评价一个人在保研上的竞争力&#xff0c;我们可以从绩点&#xff0c;课外竞赛&#xff0c;科研成果&#xff0c;学生工作&#xff0c;志愿服务等几个方面来…

Qt QTabWidget之创建标签页的多页面切换

QTabWidget 用来分页显示 重要函数: 1.void setTabText(int, QString); //设置页面的名字. 2.void setTabToolTip(QString); //设置页面的提示信息. 3.void setTabEnabled(bool); //设置页面是否被激活. 4.void setTabPosition(QTabPosition::South); //设置页面名字的位置. 5.…

游标卡尺的使用

游标卡尺的使用注意事项 游标卡尺是比较精密的测量工具&#xff0c;使用时应轻拿轻放&#xff0c;不得碰撞或跌落地下。使用时不要用来测量粗糙的物体&#xff0c;以免损坏量爪。不用时应置于干燥地方防止锈蚀。测量工件时&#xff0c;卡脚测量面必须与工件的表面平行或垂直&a…

1915_开源C语言实现的通用队列

经常在工作中遇到一些队列处理的场景&#xff0c;以前要么是借用FreeRTOS这样的系统中的相关功能&#xff0c;要么是通过数组做一个简单的队列模型。但是&#xff0c;这两种方案都具有一定的局限性能&#xff0c;前者要求的FreeRTOS不见得相应的软件中有&#xff0c;而后者只能…

【C++】什么是模板?

有不懂的地方可以翻阅我之前文章&#xff01; 个人主页&#xff1a;CSDN_小八哥向前冲 所属专栏&#xff1a;CSDN_C入门 目录 模板函数 泛型编程 函数模板 类模板 模板函数 泛型编程 在之前的学习里&#xff0c;我们知道函数可以重载&#xff0c;当我们在实现多参数函数交…

【2.2】回溯算法-解含有重复数字的全排列 II

一、题目 给定一个可包含 重复数字 的序列nums&#xff0c;按任意顺序返回所有不重复的全排列。 二、求解思路及代码实现 回溯算法思路&#xff1a; 这道题目与之前讨论的全排列问题类似&#xff0c;但有一个关键的区别&#xff1a;本题中数组包含重复的数字&#xff0c;而之前…

Springboot集成Proguard生成混淆jar包

背景 当我们需要将 JAR 包交付给第三方时&#xff0c;常常担心代码可能会被反编译。因此&#xff0c;对 JAR 包进行混淆处理显得尤为重要。 市面上有许多 JAR 包源码混淆工具&#xff0c;但真正能稳定投入使用的并不多。例如&#xff0c;ClassFinal (ClassFinal: Java字节码加…

C++类和对象1

一.类的定义 1.1类的创建 类是C中用户自己建立的类型。类似于C语言中的结构体。定义类的关键字为class。格式为&#xff1a; class 类名 {成员函数成员变量…… }; class 类名称为类头&#xff0c;花括号中的称为类体。类的声明以花括号后的分号结束&#xff0c;分号不可省…

Go-Zero微服务框架下开发接口流程

目录 一&#xff1a;定义api入参和返回值 二&#xff1a;生成入参和返回值文件 三&#xff1a;定义rpc参数和返回值 四&#xff1a;生成返回值和参数 五&#xff1a;定义数据库 六&#xff1a;生成数据库文件 今天我们来讲解下如何在Go-Zero下开发一个api接口的具体流程&…

UCOSIII内存管理机制详解

目录 前言 1. 内存管理概述 2. 内存区域&#xff08;存储区&#xff09;和内存块 3. 存储区控制块&#xff08;OS_MEM&#xff09; 4. 内存管理函数 5. 内存碎片问题 6. 注意事项 7.代码实现 7.1创建内存区域 7.2申请内存 7.3释放内存 前言 UCOSIII&#xff08;即Mi…

算法的学习笔记—正则表达式匹配的动态规划算法解析

&#x1f600;前言 正则表达式是一种强大的工具&#xff0c;广泛应用于文本匹配和处理。在许多编程任务中&#xff0c;我们可能会遇到需要匹配字符串与某个特定模式的情况。本文将介绍如何使用动态规划算法实现一个支持 . 和 * 的正则表达式匹配功能&#xff0c;并以 Java 为例…

中科院TOP“灌水神刊”合集!年发文量动辄数千篇,TOP的地位,4区的录用率!

【SciencePub学术】本期&#xff0c;给大家推荐几本环境领域的“灌水神刊”&#xff01;均隶属于中科院TOP刊之列&#xff0c;但是每年庞大的发文量致使投稿接收率极高&#xff01;话不多说&#xff0c;想“灌水”的建议收藏&#xff01; 01 年刊文量4000 Journal of Cleaner …

【C++】---红黑树详解

【C】---红黑树详解 一、什么是红黑树&#xff1f;1、概念2、性质3、四个规则 二、红黑树的定义1、红黑树 结点 定义&#xff08;1&#xff09;将新插入的结点 设置为黑色&#xff08;2&#xff09;将新插入的结点 设置为红色 2、红黑树的定义 三、红黑树插入1、插入节点2、控制…

Zabbix自动导出PDF报告

zabbix6提供了定时导出PDF报告功能。此功能可按照Dashboard维度&#xff0c;定时自动导出报告&#xff0c;并通过邮件发送。 1.安装 zabbix 提供了官方的rhel8版本的rpm包&#xff0c;可使用yum方式安装&#xff0c;zabbix自动导出PDF功能是基于go环境的zabbix web service程…

应用方案 | 低功耗接地故障控制器D4145

一、概述 D4145 是一个接地故障断路器。它能够检测到不良的接地条件&#xff0c;譬如装置接触到水时&#xff0c;它会在有害或致命的电击发生之前将电路断开。 D4145能检测并保护从火线到地线,从零线到地线的故障.这种简单而传统的电路设计能够确保其应用自如和长时间的可靠性。…