恋爱中的程序员:用 JS 代理模式给她送花

181 阅读7分钟

“恋爱就像编程,有时你得用点设计模式。”

在程序员的世界里,连恋爱也逃不过代码的套路。今天,我要讲一个程序员追女神的故事。但这个程序员不直接出手,而是通过——代理


一、对象之间的故事

场景介绍:

  • 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 的前后端通信、性能优化、安全控制等方面。

常见应用场景:

  1. 延迟加载(Lazy Load): 比如图片加载,先用代理图加载,等图片真正加载成功后再替换。

  2. 权限控制: 比如用户没有权限操作一个对象时,通过代理进行权限判断。

  3. 缓存代理: 比如前端数据请求时,对结果进行缓存,下次调用时先判断是否已有缓存。


五、恋爱进阶版:接口与抽象

我们在代理模式中,还有一个非常重要的理念就是:

面向接口编程,而不是面向实现编程。

在 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:代理对象就是一个复杂的数据结构,拥有方法。

七、总结:用模式谈恋爱,用代码表达心意

通过这个例子,我们不仅了解了代理模式的本质,还体会到了编程中抽象思维的重要性。

角色对象名方法说明
小明boysendFlower()发起行为
小红girlreceiveFlower()最终目标
小丽proxyGirlreceiveFlower()拦截、判断是否继续
'一束玫瑰'-参数传递

代理模式让程序更灵活,就像恋爱中需要判断时机,懂得等待和信任。

小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>

💡 页面运行效果:

  1. 点击「小明送花」按钮。

  2. 控制台模拟小丽判断小红心情(随机)。

  3. 结果动态展示:

    • 小红收到花(心情好);
    • 小丽保管花(心情不好)。

🧰 扩展二:代理模式 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语法运用constsetTimeout、对象方法、随机数、DOM 操作等
设计原则面向接口编程、职责分离、行为控制
可扩展性添加更多逻辑:心情类型判断、送礼品升级、缓存代理等