“恋爱就像编程,有时你得用点设计模式。”
在程序员的世界里,连恋爱也逃不过代码的套路。今天,我要讲一个程序员追女神的故事。但这个程序员不直接出手,而是通过——代理。
一、对象之间的故事
场景介绍:
- A:小明,程序员一枚,性格腼腆,不敢直接向心仪的女生表达心意。
- B:小红,小明喜欢的女生,但脾气不定,有时候心情好,有时候拒人于千里之外。
- C:小丽,小明的好朋友,小红的闺蜜,能判断小红的心情,也能“代为转交”礼物。
用一句话说就是,小明想送花给小红,但他不确定时机是否合适,于是委托小丽帮他送——这,就是代理模式(Proxy Pattern)的真实写照。
二、什么是代理模式?
代理模式是一种结构型设计模式,其核心思想是:
通过一个代理对象来控制对另一个对象的访问。
换句话说,代理对象在不改变目标对象(即被代理人)接口的情况下,为目标对象提供一个“替身”或“代表”。
在我们这个恋爱故事中,小丽就是小红的代理对象,她拥有和小红相同的接口方法,比如 receiveFlower(),但会在执行时判断是否真的要将花送出。
三、用 JavaScript 来“谈恋爱”
我们用 JavaScript 模拟这个恋爱故事:
Step 1:定义目标对象和代理对象
// 小红 - 被追求的目标对象
const girl = {
receiveFlower(flower) {
console.log('小红:收到' + flower + ',真开心!');
}
};
Step 2:定义代理对象(小丽)
const proxyGirl = {
receiveFlower(flower) {
setTimeout(function () {
// 模拟判断小红心情好坏
const mood = Math.random() > 0.5; // 随机判断
if (mood) {
girl.receiveFlower(flower);
} else {
console.log('小丽:小红现在心情不好,我帮你留着。');
}
}, 2000); // 延迟2秒再判断
}
};
Step 3:小明送花,但是通过代理
const boy = {
sendFlower(target) {
const flower = '一束玫瑰';
target.receiveFlower(flower);
}
};
// 小明通过代理对象送花
boy.sendFlower(proxyGirl);
运行这段代码后,你会发现,送花这件事并不总是直接到达小红那里,而是通过代理对象“小丽”判断时机是否合适。这就是代理模式的精髓:控制访问,延迟执行,甚至可以拦截。
四、为什么要用代理?
回到现实中的程序设计,我们为什么要用代理对象呢?除了“送花”这种浪漫用途,代理模式在实际开发中应用非常广泛,特别是在 JavaScript 的前后端通信、性能优化、安全控制等方面。
常见应用场景:
-
延迟加载(Lazy Load): 比如图片加载,先用代理图加载,等图片真正加载成功后再替换。
-
权限控制: 比如用户没有权限操作一个对象时,通过代理进行权限判断。
-
缓存代理: 比如前端数据请求时,对结果进行缓存,下次调用时先判断是否已有缓存。
五、恋爱进阶版:接口与抽象
我们在代理模式中,还有一个非常重要的理念就是:
面向接口编程,而不是面向实现编程。
在 JavaScript 中没有正式的 interface 关键字,但我们可以用对象字面量或构造函数模拟接口的行为。
例如,我们可以设定所有“送花对象”都必须有 receiveFlower() 方法,只要实现了这个接口,小明就可以放心地调用:
function sendFlowerTo(target) {
if (typeof target.receiveFlower === 'function') {
target.receiveFlower('一束香槟玫瑰');
} else {
console.error('目标无法接受花束');
}
}
这样,不论是小红还是小丽,甚至是快递员,只要他们实现了 receiveFlower 方法,就可以被“调用”来接收花。
六、和 JS 的语法结合理解
我们再回顾一下 JavaScript 基础语法,看看它们是如何服务于这个模式的:
const:用来定义不变的引用对象,比如定义 girl、proxyGirl。===:用于判断类型和值是否完全一致,比如 mood 判断。setTimeout():用来模拟延迟执行,模拟现实中“等她心情好”的行为。- 数据类型
object:代理对象就是一个复杂的数据结构,拥有方法。
七、总结:用模式谈恋爱,用代码表达心意
通过这个例子,我们不仅了解了代理模式的本质,还体会到了编程中抽象思维的重要性。
| 角色 | 对象名 | 方法 | 说明 |
|---|---|---|---|
| 小明 | boy | sendFlower() | 发起行为 |
| 小红 | girl | receiveFlower() | 最终目标 |
| 小丽 | proxyGirl | receiveFlower() | 拦截、判断是否继续 |
| 花 | '一束玫瑰' | - | 参数传递 |
代理模式让程序更灵活,就像恋爱中需要判断时机,懂得等待和信任。
小Tips:
- 代理不只是技术模式,也是生活智慧;
- 接口设计好,未来才能轻松扩展;
- 编程不止是码代码,更是抽象世界的艺术。
结尾语
在 JavaScript 的世界里,没有什么比“让对象去帮你做事”更浪漫的事了。代理模式让我们不再直接冲动,而是让代码懂得权衡、判断和等待。
就像追爱一样,有时你不是不够努力,而是时机未到。这时候,找一个好代理(像小丽),说不定,你的“receiveFlower”就会收到一个惊喜回应。
下面我们就把这个“代理送花”的恋爱故事,扩展成一个前端页面 Demo,并配上一张简单的 代理模式的 UML 图解,帮助你更形象地理解和实践。
🧱 扩展一:前端页面 Demo —— 模拟送花系统
我们做一个小页面:
- 小明点按钮送花;
- 小丽代理判断小红心情是否好;
- 页面显示结果(花有没有送成功)。
💻 HTML + CSS + JS 示例代码
✅ 1. HTML 页面结构
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>送花系统 - 代理模式演示</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
}
#log {
margin-top: 20px;
padding: 10px;
border: 1px dashed #888;
height: 150px;
overflow-y: auto;
background-color: #f9f9f9;
}
button {
padding: 10px 20px;
font-size: 16px;
}
</style>
</head>
<body>
<h1>💐 恋爱送花系统 (代理模式)</h1>
<p>小明想送花给小红,但小丽会先判断小红的心情。</p>
<button onclick="boy.sendFlower(proxyGirl)">小明送花</button>
<div id="log"></div>
<script>
function log(msg) {
const logBox = document.getElementById("log");
logBox.innerHTML += `<div>> ${msg}</div>`;
logBox.scrollTop = logBox.scrollHeight;
}
const girl = {
receiveFlower(flower) {
log(`小红:收到${flower},好开心~ 💖`);
}
};
const proxyGirl = {
receiveFlower(flower) {
log("小丽:我先看看小红心情怎么样...");
setTimeout(function () {
const moodGood = Math.random() > 0.5;
if (moodGood) {
log("小丽:小红心情很好!我替你把花送过去~");
girl.receiveFlower(flower);
} else {
log("小丽:小红现在心情不太好,我先替你保管花。");
}
}, 2000);
}
};
const boy = {
sendFlower(target) {
const flower = "一束香香的玫瑰🌹";
log("小明:我想送花给小红...");
target.receiveFlower(flower);
}
};
</script>
</body>
</html>
💡 页面运行效果:
-
点击「小明送花」按钮。
-
控制台模拟小丽判断小红心情(随机)。
-
结果动态展示:
- 小红收到花(心情好);
- 小丽保管花(心情不好)。
🧰 扩展二:代理模式 UML 图解
🎯 图中角色说明:
- Client(小明):发起行为(送花)。
- Proxy(小丽):代理控制(判断是否转交)。
- RealSubject(小红):最终接收者。
+-----------+
| 小明 |
| (Client) |
+-----------+
|
| sendFlower()
v
+-----------+
| 小丽 |
| (Proxy) |
+-----------+
|
| 判断心情后决定
v
+-----------+
| 小红 |
| (RealObj) |
+-----------+
UML 中,代理和目标对象都实现了相同的接口,因此小明无需区分是谁接花,只调用同一个方法即可。
🎁 Bonus:升级版 - 多种心情逻辑
想加点“恋爱随机性”?我们可以把 mood 判断再细化,比如:
const proxyGirl = {
receiveFlower(flower) {
log("小丽:我看看今天小红心情如何...");
setTimeout(() => {
const moods = ['happy', 'sad', 'busy', 'hungry'];
const mood = moods[Math.floor(Math.random() * moods.length)];
switch (mood) {
case 'happy':
log("小丽:小红今天特别开心!速送!");
girl.receiveFlower(flower);
break;
case 'sad':
log("小丽:小红有点难过,等会儿吧。");
break;
case 'busy':
log("小丽:她在加班中,等她有空。");
break;
case 'hungry':
log("小丽:她说饿着不想收花,先点个外卖叭🍱");
break;
}
}, 2000);
}
};
这样页面就变得更生动,每次点击都是不一样的故事 🎭。
✅ 总结
我们现在已经通过以下方式理解并实践了代理模式:
| 类型 | 内容 |
|---|---|
| 场景 | 小明 → 小丽 → 小红的送花行为控制流程 |
| 技术点 | 面向对象(对象字面量)、接口模拟、定时器、代理拦截 |
| JS语法运用 | const、setTimeout、对象方法、随机数、DOM 操作等 |
| 设计原则 | 面向接口编程、职责分离、行为控制 |
| 可扩展性 | 添加更多逻辑:心情类型判断、送礼品升级、缓存代理等 |