当前位置: 首页 > article >正文

代理模式的优缺点是什么?

什么是代理模式?

代理模式(Proxy Pattern)是一种结构型设计模式,它通过创建代理对象来控制对原始对象的访问。

这种模式在前端开发中广泛应用,特别是在需要控制对象访问、添加额外逻辑或优化性能的场景中。

​核心思想​​:在客户端代码和真实对象之间添加中间层,这个中间层(代理)可以:

  1. 控制对原始对象的访问权限
  2. 添加预处理/后处理逻辑
  3. 实现延迟加载
  4. 缓存昂贵操作的结果
  5. 记录日志或监控行为

代理模式实现方式

1. 虚拟代理(延迟加载)

// 原始图片加载器
class ImageLoader {constructor(url) {this.url = url;}load() {console.log(`Loading image from ${this.url}`);return `<img src="${this.url}" />`;}
}// 虚拟代理 - 延迟加载
class ImageProxy {constructor(url) {this.url = url;this.imageLoader = null; // 延迟初始化}load() {if (!this.imageLoader) {this.imageLoader = new ImageLoader(this.url);// 添加占位逻辑const placeholder = document.createElement('div');placeholder.style.width = '300px';placeholder.style.height = '200px';placeholder.style.background = '#eee';document.body.appendChild(placeholder);// 延迟实际加载setTimeout(() => {document.body.removeChild(placeholder);document.body.innerHTML += this.imageLoader.load();}, 2000);}return 'Image loading initiated...';}
}// 使用代理
const image = new ImageProxy('https://example.com/large-image.jpg');
console.log(image.load()); // 立即返回占位符,2秒后加载真实图片

2. 缓存代理(记忆函数)

// 原始计算函数
function fibonacci(n) {if (n <= 1) return n;return fibonacci(n - 1) + fibonacci(n - 2);
}// 缓存代理
function createCachedProxy(fn) {const cache = new Map();return function(n) {if (cache.has(n)) {console.log(`Cache hit for n=${n}`);return cache.get(n);}const result = fn(n);cache.set(n, result);console.log(`Calculated for n=${n}`);return result;};
}// 使用代理
const cachedFib = createCachedProxy(fibonacci);console.log(cachedFib(35)); // 长时间计算
console.log(cachedFib(35)); // 立即返回缓存结果

3. 保护代理(访问控制)

// 原始用户服务
class UserService {constructor() {this.users = new Map([[1, { id: 1, name: 'Admin', role: 'admin' }]]);}deleteUser(id) {this.users.delete(id);return `User ${id} deleted`;}
}// 保护代理
class UserServiceProxy {constructor(user) {this.userService = new UserService();this.currentUser = user;}deleteUser(id) {if (this.currentUser.role !== 'admin') {throw new Error('Permission denied');}return this.userService.deleteUser(id);}
}// 使用代理
const adminProxy = new UserServiceProxy({ role: 'admin' });
console.log(adminProxy.deleteUser(1)); // 成功const userProxy = new UserServiceProxy({ role: 'user' });
userProxy.deleteUser(1); // 抛出权限错误

4. ES6 Proxy实现

// 原始对象
const database = {users: {1: { name: 'Alice', email: 'alice@example.com' }},getEmail: function(userId) {return this.users[userId]?.email;}
};// 创建代理
const protectedDatabase = new Proxy(database, {get(target, prop) {// 权限验证if (prop === 'users') {throw new Error('Direct access to users denied');}return target[prop];},set(target, prop, value) {// 写操作限制if (prop === 'users') {throw new Error('User modification requires admin privileges');}target[prop] = value;return true;}
});console.log(protectedDatabase.getEmail(1)); // 正常访问
protectedDatabase.users = {}; // 抛出错误
console.log(protectedDatabase.users); // 抛出错误

代理模式优缺点分析

优点:

  1. ​访问控制​​:实现精细的权限管理
// API请求代理示例
class ApiProxy {constructor(api) {this.api = api;}async request(endpoint) {if (isRateLimited(endpoint)) {throw new Error('API rate limit exceeded');}trackRequest(endpoint);return this.api.request(endpoint);}
}
  1. ​性能优化​​:通过缓存和延迟加载提升性能
// 图片懒加载代理
const lazyImage = new Proxy(new Image(), {set(target, prop, value) {if (prop === 'src') {// 延迟到元素可见时加载const observer = new IntersectionObserver((entries) => {entries.forEach(entry => {if (entry.isIntersecting) {target.src = value;observer.unobserve(target);}});});observer.observe(target);return true;}return Reflect.set(...arguments);}
});document.body.appendChild(lazyImage);
lazyImage.src = 'https://example.com/large-image.jpg'; // 实际加载延迟到图片可见时
  1. ​职责分离​​:保持核心逻辑的纯净性
// 原始表单验证
class FormValidator {validateEmail(email) {return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);}
}// 验证代理添加日志
class LoggingValidatorProxy {constructor(validator) {this.validator = validator;}validateEmail(email) {const result = this.validator.validateEmail(email);console.log(`Email validation result for ${email}: ${result}`);return result;}
}

缺点:

  1. ​复杂性增加​​:可能引入额外抽象层
// 过度设计的代理示例(不推荐)
class OverEngineeredProxy {constructor(service) {this.service = service;this.logger = new Logger();this.cache = new Cache();this.validator = new Validator();}async getData() {this.logger.logStart();if (!this.validator.validate()) {throw new Error('Validation failed');}const data = await this.cache.get('data') || this.service.getData();this.logger.logEnd();return data;}
}
  1. ​性能损耗​​:额外的代理调用开销
// 性能敏感的原始类
class Vector {constructor(x, y) {this.x = x;this.y = y;}add(other) {return new Vector(this.x + other.x, this.y + other.y);}
}// 添加日志代理可能影响性能
const loggedVector = new Proxy(new Vector(1,2), {get(target, prop) {if (typeof target[prop] === 'function') {return function(...args) {console.log(`Calling ${prop} with`, args);return target[prop].apply(target, args);};}return target[prop];}
});// 在需要高性能计算的场景中,这种代理会产生明显开销
  1. ​调试困难​​:调用堆栈变深
// 多层代理导致的调试问题
const original = { method() { console.log('Original method'); } 
};const proxy1 = new Proxy(original, {get(target, prop) {console.log('Proxy1 handler');return target[prop];}
});const proxy2 = new Proxy(proxy1, {get(target, prop) {console.log('Proxy2 handler');return target[prop];}
});proxy2.method(); // 调用链:proxy2 -> proxy1 -> original

工程实践建议

1. 表单验证代理

// 原始表单对象
const form = {values: {},errors: {},submit() {console.log('Submitting:', this.values);}
};// 验证代理
const validatedForm = new Proxy(form, {set(target, prop, value) {if (prop === 'values') {// 自动触发验证target.errors = validateForm(value);if (Object.keys(target.errors).length === 0) {target.submit();}}return Reflect.set(...arguments);}
});function validateForm(values) {const errors = {};if (!values.email?.includes('@')) errors.email = 'Invalid email';if (values.password?.length < 6) errors.password = 'Password too short';return errors;
}// 使用
validatedForm.values = { email: 'user@example.com', password: '12345' 
}; // 自动触发验证并显示错误

2. API请求代理

// 请求代理工厂
function createApiProxy(api, config = {}) {return new Proxy(api, {get(target, prop) {const originalMethod = target[prop];if (typeof originalMethod !== 'function') return originalMethod;return async function(...args) {// 请求拦截if (config.beforeRequest) {args = config.beforeRequest(...args) || args;}try {const response = await originalMethod.apply(target, args);// 响应拦截return config.afterResponse ? config.afterResponse(response) : response;} catch (error) {// 错误处理if (config.errorHandler) {return config.errorHandler(error);}throw error;}};}});
}// 使用示例
const rawApi = {async getUser(id) {const res = await fetch(`/api/users/${id}`);return res.json();}
};const enhancedApi = createApiProxy(rawApi, {beforeRequest: (id) => {console.log(`Requesting user ${id}`);return [id]; // 可以修改参数},afterResponse: (data) => {console.log('Received response');return { ...data, fullName: `${data.firstName} ${data.lastName}` };},errorHandler: (error) => {console.error('API Error:', error);return { error: true, message: error.message };}
});// 调用方式保持一致
enhancedApi.getUser(123).then(console.log);

注意事项

  1. ​接口一致性​​:代理必须保持与原对象相同的接口
// 错误的代理实现(接口不一致)
class BadProxy {constructor(file) {this.file = file;}// 遗漏了原始对象的save方法read() {return this.file.read();}
}
  1. ​避免深层代理嵌套​
// 不推荐的深层嵌套
const proxy1 = new Proxy(obj, handler1);
const proxy2 = new Proxy(proxy1, handler2);
const proxy3 = new Proxy(proxy2, handler3);
// 应尽量保持代理层级扁平
  1. ​注意内存管理​
// 代理导致的闭包内存泄漏
function createLeakyProxy() {const hugeData = new Array(1000000).fill('data');return new Proxy({}, {get(target, prop) {// 无意中持有hugeData的引用return hugeData[prop];}});
}
  1. ​性能关键路径慎用​
// 在动画循环中避免使用复杂代理
function animate() {// 直接访问对象属性element.x += velocity.x;element.y += velocity.y;// 而不是:// proxiedElement.x += velocity.x;requestAnimationFrame(animate);
}
  1. ​与装饰器模式区分​
// 装饰器模式(增强功能)
function withLogging(fn) {return function(...args) {console.log('Calling function');return fn(...args);};
}// 代理模式(控制访问)
const proxiedFn = new Proxy(fn, {apply(target, thisArg, args) {if (!validate(args)) throw new Error('Invalid arguments');return Reflect.apply(target, thisArg, args);}
});

代理模式是前端架构中的重要模式,适用于:

  • 需要访问控制的场景(权限验证、流量控制)
  • 性能优化需求(缓存、延迟加载)
  • 增强监控能力(日志记录、性能跟踪)
  • 实现智能引用(自动清理、加载)

在实际工程中建议:

  1. 优先使用ES6 Proxy实现简单代理逻辑
  2. 对性能敏感模块谨慎使用
  3. 保持代理接口与原始对象一致
  4. 使用TypeScript增强类型安全
  5. 配合工厂模式创建复杂代理

正确使用代理模式可以提升代码的可维护性和扩展性,但需要警惕模式滥用带来的复杂性。

建议结合具体需求场景,在代码清晰度和功能需求之间找到平衡点。

相关文章:

代理模式的优缺点是什么?

什么是代理模式&#xff1f; 代理模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过创建代理对象来控制对原始对象的访问。 这种模式在前端开发中广泛应用&#xff0c;特别是在需要控制对象访问、添加额外逻辑或优化性能的场景中。 ​​核心…...

基于LangChain和通义(Tongyi)实现NL2SQL的智能检索(无需训练)

在数据驱动的时代,如何高效地从数据库中获取信息成为了一个重要的挑战。自然语言到SQL(NL2SQL)技术提供了一种便捷的解决方案,使用户能够用自然语言查询数据库,而无需深入了解SQL语法。本文将探讨如何利用LangChain和通义(Tongyi)实现NL2SQL的智能检索,具体步骤如下: …...

Spring Boot 集成 Redis 对哈希数据的详细操作示例,涵盖不同结构类型(基础类型、对象、嵌套结构)的完整代码及注释

以下是 Spring Boot 集成 Redis 对哈希数据的详细操作示例&#xff0c;涵盖不同结构类型&#xff08;基础类型、对象、嵌套结构&#xff09;的完整代码及注释&#xff1a; 1. 集成步骤 1.1 添加依赖 在 pom.xml 中添加以下依赖&#xff1a; <dependency><groupId&g…...

ROS云课三分钟-差动移动机器人巡逻报告如何撰写-中等报告

评语&#xff1a; 成绩中等&#xff08;70/100&#xff09;&#xff0c;具体如下&#xff1a; 1. 摘要部分 问题描述&#xff1a; 内容空洞&#xff1a;摘要过于简短&#xff0c;仅简要概述了研究内容和实现方法&#xff0c;未突出研究的创新点或重要性。缺乏细节&#xff1…...

Java8+Spring Boot + Vue + Langchain4j 实现阿里云百炼平台 AI 流式对话对接

1. 引言 在本文中&#xff0c;我们将介绍如何使用 Spring Boot、Vue.js 和 Langchain4j&#xff0c;实现与 阿里云百炼平台 的 AI 流式对话对接。通过结合这些技术&#xff0c;我们将创建一个能够实时互动的 AI 聊天应用。 这是一个基于 Spring Boot Vue.js Langchain4j 的智…...

可发1区的超级创新思路(python 实现):一种轻量化的动态稀疏门控网络

首先声明,该模型为原创!原创!原创!且该思路还未有成果发表,感兴趣的小伙伴可以借鉴! 一、应用领域 视频异常检测、生成视频检测。 二、模型解析 该模型由1.关键帧动态选择机制、2.关键帧动态选择机制以及3.关键帧动态选择机制三大核心组件构成,形成端到端的视频异常…...

【Kafka基础】单机安装与配置指南,从零搭建环境

学习Kafka&#xff0c;掌握Kafka的单机部署是理解其分布式特性的第一步。本文将手把手带你完成Kafka单机环境的安装、配置及基础验证&#xff0c;涵盖常见问题排查技巧。 1 环境准备 1.1 系统要求 操作系统&#xff1a;CentOS 7.9依赖组件&#xff1a;JDK 8&#xff08;Kafka …...

Scala 转义字符

Scala 转义字符 引言 Scala作为一种多范式编程语言&#xff0c;拥有丰富的字符处理能力。在Scala编程中&#xff0c;转义字符的使用非常频繁&#xff0c;它们可以用来处理字符串中的特殊字符&#xff0c;使得字符串的表示更加直观和符合预期。本文将详细探讨Scala中的转义字符…...

TCP/IP五层协议

目录 1. 五层模型结构 2. 各层核心功能与协议 (1) 应用层&#xff08;Application Layer&#xff09; (2) 传输层&#xff08;Transport Layer&#xff09; (3) 网络层&#xff08;Network Layer&#xff09; (4) 数据链路层&#xff08;Data Link Layer&#xff09; (5…...

Dify接口api对接,流式接收流式返回(.net)

试了好多种方法除了Console.WriteLine()能打印出来&#xff0c;试了好些方法都不行&#xff0c;不是报错就是打印只有一行&#xff0c;要么就是接收完才返回...下面代码实现调用api接收流式数据&#xff0c;并进行流式返回给前端&#xff1a; using Furion.HttpRemote; using …...

微信小程序开发前端培训课程

大前端培训课程 1.HTML课程&#xff1a; 1.HTML标签基础 2.布局DIVspan 3.表单标签 4.多媒体标签 5.Table使用 2.CSS课程&#xff1a; 1.Box 盒子模型&#xff0c;列表布局&#xff08;一行两列&#xff0c;一行多列&#xff09; 2.单行文字&#xff0c;多行文字 3.文…...

代码随想录算法训练营第五十二天|图论专题: 101. 孤岛的总面积、102. 沉没孤岛、103. 水流问题、104. 建造最大岛屿

101. 孤岛的总面积 本题要求找到不靠边的陆地面积&#xff0c;那么我们只要从周边找到陆地然后 通过 dfs或者bfs 将周边靠陆地且相邻的陆地都变成海洋&#xff0c;然后再去重新遍历地图 统计此时还剩下的陆地就可以了。 1、从左边和后边向中间遍历 2、从上边和下边向中间遍历…...

仿modou库one thread one loop式并发服务器

源码&#xff1a;田某super/moduo 目录 SERVER模块&#xff1a; Buffer模块&#xff1a; Socket模块&#xff1a; Channel模块&#xff1a; Connection模块&#xff1a; Acceptor模块&#xff1a; TimerQueue模块&#xff1a; Poller模块&#xff1a; EventLoop模块&a…...

MNIST 数据集 与 TFOD API

此处给出我在进行毕业设计过程中写的三份脚本&#xff0c;作为demo 展示模型的预处理&#xff0c;输出信息提取和TFOD API的应用。 script1 加载本地的MNIST模型&#xff0c;对本地的手写数字进行推理 # test the validation of the saved file and the camera import cv2 i…...

SpringSecurity6.0 通过JWTtoken进行认证授权

之前写过一个文章&#xff0c;从SpringSecurity 5.x升级到6.0&#xff0c;当时是为了配合公司的大版本升级做的&#xff0c;里面的各项配置都是前人留下来的&#xff0c;其实没有花时间进行研究SpringSecurity的工作机制。现在新东家有一个简单的系统要搭建&#xff0c;用户的认…...

【Java】Maven

一、概念 是一个项目管理和构建工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff09;的概念&#xff0c;通过一小段描述信息来管理项目的构建。 二、Maven坐标 <groupId>com.itheima</groupId><artifactId>maven-project01</artifactId>&…...

第十五届蓝桥杯PythonC组题解

A题&#xff1a;拼正方形 问题描述 给定一定数量的 22 和 11 的方块&#xff0c;求能拼出的最大正方形边长。 解题思路 二分法&#xff1a;将奇数和偶数边长分开处理&#xff0c;通过二分法寻找最大满足条件的边长。面积验证&#xff1a;总方块面积需大于等于目标正方形面积…...

MATLAB中plot函数的详细参数表

LineSpec - 线型、标记和颜色 线型说明-实线--虚线:点线-.点划线 标记说明o圆圈加号*星号.点x叉号_水平线条|垂直线条s方形d菱形^上三角v下三角>右三角<左三角p五角形h六角形 颜色说明 y 黄色 m 品红色 c 青蓝色 r 红色 g 绿色 b 蓝色 w 白色 k 黑色 MarkerFaceColor…...

R语言赋能气象水文科研:从多维数据处理到学术级可视化

全球气候变化加剧了极端天气与水文事件的复杂性&#xff0c;气象卫星、雷达、地面观测站及水文传感器每天产生TB级‌时空异质数据‌。传统研究常面临四大瓶颈&#xff1a; ‌数据清洗低效‌&#xff1a;缺失值、异常值处理耗时&#xff1b;‌时空分析模型构建复杂‌&#xff1…...

虚拟试衣间-云尚衣橱小程序-衣橱管理实现

衣橱管理实现 目标 (Goal): 用户 (User): 能通过 UniApp 小程序上传衣服图片。 后端 (Backend): 接收图片,存到云存储,并将图片信息(URL、用户ID等)存入数据库。 用户 (User): 能在小程序里看到自己上传的所有衣服图片列表。 技术栈细化 (Refined Tech Stack for this Pha…...

BGP路由协议之属性2

Orgin 起源 公认必遵属性 起源名称标记描述IGPi如果路由是由始发的 BGP 路由器使用 network 命令注入到 BGP 的&#xff0c;那么该 BGP 路由的 origin 属性为 IGPEGPe如果路由是通过 EGP 学习到的&#xff0c;那么该 BGP 路由的 Origin 属性为 EGPIncomplete?如果路由是通过…...

纯个人整理,蓝桥杯使用的算法模板day2(0-1背包问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个

算法索引 01背包优化前空间优化版&#xff08;使用一维数组&#xff09;优化后的模拟流程图为何优化后&#xff0c;j不能使用正序遍历模拟流程图 代码对应实现案例 01背包 优化前 /*** 0-1背包问题解法&#xff08;与下方代码表格示例对应&#xff0c;已模拟验证&#xff09;*…...

算法与数据结构线性表之栈和队列

Hello大家好&#xff01; 很高兴与大家见面&#xff01; 给生活添点快乐&#xff0c;开始今天的编程之路。 我的博客:<但愿. 我的专栏:C语言、题目精讲、算法与数据结构、C 欢迎点赞&#xff0c;关注 一 栈 1概念&#xff1a;栈是⼀种特殊的线性表&#xff0c;其只允许…...

python应用之使用pdfplumber 解析pdf文件内容

目录标题 一. 通过 pdfplumber.open() 解析复杂PDF&#xff1a;1-2. 报错&#xff1a;V2 &#xff1a; 1-3. v3 使用tk 库&#xff0c;弹框选择文件运行环境准备完整代码保存运行测试步骤方式二&#xff1a;命令行方式&#xff08;适用于自动化&#xff09; 测试用例示例常见问…...

laravel update报In PackageManifest.php line 122:Undefined index: name 错误的解决办法

用 composer 更新 laravel依赖包时报错 > Illuminate\Foundation\ComposerScripts::postAutoloadDump > Illuminate\Foundation\ComposerScripts::postAutoloadDump > php artisan package:discover --ansiIn PackageManifest.php line 122:Undefined index: nameScr…...

Vue中使用antd-table组件实现数据选择、禁用、已选择禁用-demo

实现案例 实现过程 表格代码 关键代码 :row-selection="rowSelection" <div><div class="flex items-center justify-between pt-[24px] pb-[16px]"><p>已选:{{ keysNum }}</p><a-input-search v-model:value="productN…...

C语言--统计输入字符串中的单词个数

输入 输入&#xff1a;大小写字母以及空格&#xff0c;单词以空格分隔 输出&#xff1a;单词个数 代码 如果不是空格且inWord0说明是进入单词的第一个字母&#xff0c;则单词总数加一。 如果是空格&#xff0c;证明离开单词&#xff0c;inWord 0。 #include <stdio.h&g…...

Kubernetes 集群搭建(三):使用dashboard用户界面(需要访问外网获取yaml)

&#xff08;一&#xff09;简介 K8s Dashboard是Kubernetes提供的一种基于Web的用户界面工具&#xff0c;用于可视化地管理和监控Kubernetes集群 主要功能&#xff1a; 资源查看与管理&#xff1a; 查看Kubernetes集群中的各种资源&#xff0c;如节点、Pod、服务、部署等。 对…...

Debian 12 服务器搭建Beego环境

一、Debian 12系统准备 1.更新系统 #apt update && apt upgrade -y 2.安装基础工具 #apt install -y git curl wget make gcc 二、安装Go环境 Go语言的镜像官网&#xff1a;https://golang.google.cn/ 1.下载go最新版 #cd /usr/local/src #wget -o https://golang.go…...

游戏引擎学习第208天

运行游戏并回顾我们的情况 今天&#xff0c;我们将继续完成之前中断的调试输出工作。最近的工作偏离了一些&#xff0c;展示了如何进行元编程的实践&#xff0c;主要涉及了一个小的解析器。尽管这个解析器本身是一个玩具&#xff0c;但它展示了如何完成一个完整的循环&#xf…...