找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1531

积分

0

好友

225

主题
发表于 4 天前 | 查看: 15| 回复: 0

不止学是什么,还要学为什么

不止学是什么,还要学为什么

以前一提到设计模式,就觉得很神圣,现在重新回头一归纳总结。现在想想,其实不就相当于生活经验吗?

为什么说设计模式是“生活经验”?

首先要明确,设计模式主要是针对面向对象设计中常见问题的解决方案。什么是面向对象?简单来说,它来源于对现实世界的模拟。

现实世界可以从两方面理解:

  • 静态的世界:由不同事物组成,如动物、植物、空气。
  • 动态的世界:是不同事物间交互的过程,如交流、开车、吃饭。

于是面向对象认为,任何软件都可以看作是 “由不同事物组成” 以及 “不同事物间彼此交互” 。例如在一个商城中,用户、商品、订单都是“事物”,而用户下单的行为,则是用户与商品两个“事物”交互的过程,这个交互还会产生一个新“事物”——订单。

所以,软件即世界

那么既然:

  • 面向对象把软件当做世界
  • 设计模式主要针对面向对象设计
  • 设计模式是软件问题的解决方案

那么,世界问题的解决方案的通俗叫法是什么?可不就是生活经验嘛!

生活经验比喻

设计模式的分类逻辑

世界是【不同事物组成】+【不同事物交互】。对于“事物组成”,我们通常会思考两个问题:

  1. 怎么去创建事物更快更合理?
  2. 怎么给事物分类更合理?

对于“事物交互”,则会想:

  1. 怎么让事物彼此交互更合理?

与此完美对应,设计模式也分为了三类:

  • 创建型:主要解决对象创建的问题
  • 结构型:解决类与对象的组织结构问题
  • 行为型:解决类和对象通信协作问题

这就不谋而合了!

设计模式分类对应关系

创建型模式好比流水线。当我们需要频繁创建流程复杂但又重复的东西时,就会想到建立标准流程。就像连锁品牌,把开店流程标准化,实现快速复制。

结构型模式好比整理分类。当事物越来越多,人类总会想着如何更合理地进行分类,以便更好地管理和研究,如同生物分类法(界、门、纲、目、科、属、种)。

行为型模式好比优化交互的套餐。人类历史一直致力于优化事物间的交互以提升效率。例如餐厅的“套餐”,将多个菜品组合,极大地简化了顾客点餐与服务员的交互过程。

所有的设计模式,都是为了减少出问题的概率。修改范围越小,影响就越小,出问题的概率自然就低。你一定说过:“我都没改!怎么可能出问题!”


一、创建型模式

主要解决对象创建的问题。当对象的创建流程复杂但又相对固定时,就适合使用这类模式。共5种:工厂模式、抽象工厂模式、原型模式、单例模式、构建器模式。

1. 工厂模式 (Factory Method)

定义:定义一个创建对象的接口,由子类决定需要实例化哪一个类。
工厂模式示意图

当一个类中可能存在多个相同类型的操作,但具体实现有差异时,就把这些 “差异部分” 交给子类去实现,父类只实现 “相同的部分”

例如,一个支付类,支付流程大体相同,但存在支付宝、微信等不同渠道。工厂方法模式就会把【不同渠道的实现】放在子类,父类中只定义一个抽象方法作为占位。

父类实现相同骨架:

class Payment {
  pay(amount) {
    // 相同的预处理逻辑...
    const result = this._createPayment(amount); // 差异部分,子类实现
    // 相同的后续处理逻辑...
    return result;
  }
  // 差异部分,交给子类
  _createPayment(amount) {
    throw new Error('子类必须实现此方法');
  }
}

子类实现差异部分:

class AlipayPayment extends Payment {
  _createPayment(amount) {
    // 调用支付宝SDK: alipay.trade.create(...)
    console.log(`支付宝支付${amount}元`);
    return { success: true, channel: 'alipay' };
  }
}

class WechatPayment extends Payment {
  _createPayment(amount) {
    // 调用微信支付SDK: requestPayment(...)
    console.log(`微信支付${amount}元`);
    return { success: true, channel: 'wechat' };
  }
}

核心思想:把变化的部分推迟到子类,而不变的部分保留在父类中。注意,并非所有继承都是工厂模式,关键在于 【子类实现差异部分】+【父类实现相同部分】

2. 抽象工厂模式 (Abstract Factory)

定义:提供一个接口,可以创建一系列相关的对象,而无需指定它们具体的类。
抽象工厂模式示意图

与工厂模式很像,但核心区别在于:抽象工厂是创建一系列相关对象

什么是一系列相关对象? 例如一台台式电脑,由显示器、主机、键盘、鼠标组成,它们各不相同但彼此协作。首先定义这些抽象产品:

// 抽象产品
class Monitor { /* ... */ }
class Host { /* ... */ }
class Keyboard { /* ... */ }
class Mouse { /* ... */ }

// 具体产品 - 戴尔品牌
class DellMonitor extends Monitor { /* ... */ }
class DellHost extends Host { /* ... */ }
// ... 其他戴尔配件

// 具体产品 - 联想品牌
class LenovoMonitor extends Monitor { /* ... */ }
class LenovoHost extends Host { /* ... */ }
// ... 其他联想配件

如果不用抽象工厂,客户端需要分别实例化所有配件,切换品牌时需要修改多处代码,容易出错。

// 不好的方式:直接依赖具体类
const monitor = new DellMonitor();
const host = new DellHost();
const keyboard = new DellKeyboard();
const mouse = new DellMouse();
// 想换联想?得改四个地方!

抽象工厂模式引入一个 “组合工厂”,负责生产一整套相关联的产品。

// 抽象工厂接口
class ComputerFactory {
  createMonitor() { throw new Error('必须实现'); }
  createHost() { throw new Error('必须实现'); }
  createKeyboard() { throw new Error('必须实现'); }
  createMouse() { throw new Error('必须实现'); }
}

// 具体工厂 - 戴尔
class DellFactory extends ComputerFactory {
  createMonitor() { return new DellMonitor(); }
  createHost() { return new DellHost(); }
  createKeyboard() { return new DellKeyboard(); }
  createMouse() { return new DellMouse(); }
}

// 具体工厂 - 联想
class LenovoFactory extends ComputerFactory {
  createMonitor() { return new LenovoMonitor(); }
  createHost() { return new LenovoHost(); }
  createKeyboard() { return new LenovoKeyboard(); }
  createMouse() { return new LenovoMouse(); }
}

客户端使用:

// 只需切换工厂类,所有配件品牌一起换
function assembleComputer(factory) {
  const monitor = factory.createMonitor();
  const host = factory.createHost();
  const keyboard = factory.createKeyboard();
  const mouse = factory.createMouse();
  return { monitor, host, keyboard, mouse };
}

const myDellComputer = assembleComputer(new DellFactory());
const myLenovoComputer = assembleComputer(new LenovoFactory());

抽象在哪里? 客户端代码(assembleComputer)仅依赖于ComputerFactory这个抽象接口,不与任何具体的品牌工厂类(DellFactory, LenovoFactory)耦合。可以理解为 工厂方法模式 + 抽象的组合接口

3. 原型模式 (Prototype)

定义:通过拷贝现有对象来创建新对象,而无需知道其具体类。

说人话:不通过new来创建对象,而是通过“克隆”。

为什么用?

  1. 提升效率:某些对象(如数据库连接)创建成本高,克隆可以避免重复的昂贵初始化。
  2. 减少类的数目:当对象间只有少数属性差异时,不必为每个变体都创建一个新类。

假设有多种用户配置模板:

// 原型对象
const userPrototype = {
  name: '默认用户',
  role: 'guest',
  permissions: ['read'],
  clone() {
    // 浅拷贝示例,实际可能需要深拷贝
    return Object.create(this, Object.getOwnPropertyDescriptors(this));
  }
};

// 通过克隆创建新对象,并修改差异部分
const adminUser = userPrototype.clone();
adminUser.role = 'admin';
adminUser.permissions = ['read', 'write', 'delete'];

const vipUser = userPrototype.clone();
vipUser.role = 'vip';
vipUser.permissions = ['read', 'write'];

核心:提供了一种新的对象创建思路——从 “创建类 + new” 转变为 “创建对象 + clone”。在原型模式的世界观里,所有对象都来自对另一个对象的复制。

4. 单例模式 (Singleton)

定义:确保一个类只有一个实例,并提供全局访问点。

class Logger {
  constructor() {
    if (Logger.instance) {
      return Logger.instance;
    }
    this.logs = [];
    Logger.instance = this;
  }
  log(message) {
    this.logs.push(message);
    console.log(`[LOG]: ${message}`);
  }
  getLogs() {
    return this.logs;
  }
}
// 确保构造器只执行一次
const logger1 = new Logger();
const logger2 = new Logger();
console.log(logger1 === logger2); // true

本质原因

  • 保证数据一致性:如全局配置、缓存。
  • 防止行为冲突:如日志管理、数据库连接池。

就像公司只有一个公共账本,所有人都在这上面记录,避免数据混乱。

5. 构建器模式 (Builder)

定义:将复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

场景:当对象构造逻辑复杂(涉及大量校验或步骤),且需要支持多种变体时。

传统方式的问题

class User {
  constructor(name, age, email) {
    // 构造与校验耦合在一起,臃肿
    if (!name) throw new Error('姓名必填');
    if (age < 0 || age > 150) throw new Error('年龄无效');
    if (!this._isValidEmail(email)) throw new Error('邮箱无效');
    this.name = name;
    this.age = age;
    this.email = email;
  }
  _isValidEmail(email) { /* ... */ }
}
// 属性多起来后,构造函数会非常恐怖

构建器模式解决方案

// 1. 纯净的数据类(表示)
class User {
  constructor(name, age, email) {
    this.name = name;
    this.age = age;
    this.email = email;
  }
}

// 2. 构建器(构建逻辑)
class UserBuilder {
  constructor() {
    this.user = new User('', 0, '');
  }
  setName(name) {
    if (!name) throw new Error('姓名必填');
    this.user.name = name;
    return this; // 支持链式调用
  }
  setAge(age) {
    if (age < 0 || age > 150) throw new Error('年龄无效');
    this.user.age = age;
    return this;
  }
  setEmail(email) {
    if (!this._isValidEmail(email)) throw new Error('邮箱无效');
    this.user.email = email;
    return this;
  }
  _isValidEmail(email) { /* ... */ }
  build() {
    // 最终构建前可做整体校验
    return this.user;
  }
}

// 3. 导演类(可选,封装固定构建流程)
class UserDirector {
  buildDefaultUser() {
    return new UserBuilder()
      .setName('默认用户')
      .setAge(18)
      .setEmail('default@example.com')
      .build();
  }
  buildVIPUser(name) {
    return new UserBuilder()
      .setName(name)
      .setAge(30)
      .setEmail('vip@example.com')
      .build();
  }
}

// 使用
const user1 = new UserBuilder()
  .setName('张三')
  .setAge(25)
  .setEmail('zhangsan@example.com')
  .build();

const director = new UserDirector();
const defaultUser = director.buildDefaultUser();

优点:将构造逻辑与数据表示解耦,便于复用不同的构建逻辑,也使调用代码更清晰。


二、结构型模式

解决系统中类与对象的组织结构问题。通过对对象进行组合,实现新功能。共7种:适配器、桥接、组合、装饰、外观、享元、代理。

6. 适配器模式 (Adapter)

定义:将一个类的接口转换成客户希望的另一个接口,使原本不兼容的类可以一起工作。

生活中的例子:电源转接头。
适配器模式生活例子

代码示例:统一不同支付渠道的接口。

// 目标接口(我们希望的)
class Payment {
  pay(amount) { throw new Error('必须实现'); }
}

// 被适配者1:支付宝SDK(接口不同)
class AlipaySDK {
  alipayTradeCreate(amount) {
    console.log(`支付宝创建交易: ${amount}`);
    return { tradeNo: 'ali123' };
  }
}
// 被适配者2:微信支付SDK(接口不同)
class WechatPaySDK {
  requestPayment(amount) {
    console.log(`微信支付请求: ${amount}`);
    return { transactionId: 'wx456' };
  }
}

// 适配器
class AlipayAdapter extends Payment {
  constructor() {
    super();
    this.alipay = new AlipaySDK();
  }
  pay(amount) {
    // 适配:将pay方法转换为alipayTradeCreate
    return this.alipay.alipayTradeCreate(amount);
  }
}

class WechatPayAdapter extends Payment {
  constructor() {
    super();
    this.wechatPay = new WechatPaySDK();
  }
  pay(amount) {
    // 适配:将pay方法转换为requestPayment
    return this.wechatPay.requestPayment(amount);
  }
}

// 客户端统一调用
const payments = [new AlipayAdapter(), new WechatPayAdapter()];
payments.forEach(p => p.pay(100));

与工厂模式区别

  • 工厂模式:创建时发现差异,交给子类实现(恋爱结婚,水到渠成)。
  • 适配器模式:一开始就为了兼容不同接口而创建(目的明确,为结合而生)。
7. 桥接模式 (Bridge)

定义:将抽象部分与它的实现部分分离,使它们都可以独立地变化。核心:用组合代替继承

问题场景:游戏人物换装。如果用继承:

class MonkeyKingWithGoldenCudgel { /* 悟空+金箍棒 */ }
class MonkeyKingWithLockArmor { /* 悟空+锁子甲 */ }
class MonkeyKingWithBoth { /* 悟空+金箍棒+锁子甲 */ }
// 为N种武器和M种衣服,需要创建 N*M 个类,类爆炸!

桥接模式解决:将人物与装备解耦,通过组合动态搭配。

// 实现部分:装备接口
class Weapon {
  use() { throw new Error('必须实现'); }
}
class Armor {
  wear() { throw new Error('必须实现'); }
}
// 具体装备
class GoldenCudgel extends Weapon {
  use() { console.log('挥舞金箍棒!'); }
}
class LockArmor extends Armor {
  wear() { console.log('穿上锁子甲!'); }
}

// 抽象部分:人物
class Character {
  constructor(weapon, armor) {
    this.weapon = weapon;
    this.armor = armor;
  }
  fight() {
    this.weapon.use();
  }
  defend() {
    this.armor.wear();
  }
}

// 组合使用
const wukong = new Character(new GoldenCudgel(), new LockArmor());
wukong.fight(); // 挥舞金箍棒!
wukong.defend(); // 穿上锁子甲!

// 轻松更换装备
const anotherWukong = new Character(new GoldenCudgel(), null);

通过 组合,避免了因多维度变化(武器、衣服、坐骑...)而导致的类数量指数级增长。

8. 组合模式 (Composite)

定义:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。

场景:商品与套餐。单个商品和套餐(由多个商品组成)都需要计算价格。
传统问题:需要不断判断对象类型。

// 不好的方式
function calculatePrice(item) {
  if (item instanceof SingleProduct) {
    return item.price;
  } else if (item instanceof Combo) {
    let total = 0;
    for (let product of item.products) {
      total += calculatePrice(product); // 递归,且要判断
    }
    return total * item.discount;
  } else if (item instanceof Gift) {
    // ... 更多判断
  }
  // if-else 会越来越长
}

组合模式解决:定义统一接口,让“部分”和“整体”行为一致。

// 组件接口
class ProductComponent {
  getPrice() { throw new Error('必须实现'); }
}

// 叶子节点:单个商品
class SingleProduct extends ProductComponent {
  constructor(price) {
    super();
    this.price = price;
  }
  getPrice() {
    return this.price;
  }
}

// 复合节点:套餐
class Combo extends ProductComponent {
  constructor(discount = 1) {
    super();
    this.products = [];
    this.discount = discount;
  }
  add(product) {
    this.products.push(product);
  }
  getPrice() {
    let total = 0;
    for (let product of this.products) {
      total += product.getPrice(); // 统一接口,无需判断类型
    }
    return total * this.discount;
  }
}

// 使用
const burger = new SingleProduct(20);
const fries = new SingleProduct(10);
const drink = new SingleProduct(8);

const mealCombo = new Combo(0.9); // 九折套餐
mealCombo.add(burger);
mealCombo.add(fries);
mealCombo.add(drink);

console.log(burger.getPrice()); // 20
console.log(mealCombo.getPrice()); // (20+10+8)*0.9 = 34.2
// 可以继续组合
const superCombo = new Combo(0.8);
superCombo.add(mealCombo);
superCombo.add(new SingleProduct(15));

核心价值统一接口,消除部分与整体的差异,使客户端代码简洁,并能轻松应对树形结构。

为什么叫组合模式? 因为它针对的是 “树形结构” 这一特定场景。就像“好好学习”是抽象建议,而“数学多做习题,语文多背诵”是针对具体科目的策略。

9. 装饰模式 (Decorator)

定义:动态地给一个对象添加一些额外的职责,而不改变其结构。

场景:给支付操作添加日志记录。
传统紧耦合方式

class Payment {
  pay(amount) {
    console.log(`开始支付: ${amount}`); // 日志和支付耦合
    // ... 核心支付逻辑
    console.log(`支付完成: ${amount}`);
  }
}

装饰模式:分离核心逻辑与附加功能。

// 组件接口
class Payment {
  pay(amount) { throw new Error('必须实现'); }
}

// 具体组件
class BasicPayment extends Payment {
  pay(amount) {
    console.log(`执行核心支付逻辑: ${amount}`);
    return { success: true };
  }
}

// 装饰器基类
class PaymentDecorator extends Payment {
  constructor(payment) {
    super();
    this.payment = payment; // 组合一个支付对象
  }
  pay(amount) {
    return this.payment.pay(amount);
  }
}

// 具体装饰器:日志装饰器
class LoggingDecorator extends PaymentDecorator {
  pay(amount) {
    console.log(`[LOG] 支付开始: ${amount}`);
    const result = super.pay(amount);
    console.log(`[LOG] 支付结束,结果: ${result.success}`);
    return result;
  }
}
// 具体装饰器:性能监控装饰器
class MonitoringDecorator extends PaymentDecorator {
  pay(amount) {
    const start = Date.now();
    const result = super.pay(amount);
    const duration = Date.now() - start;
    console.log(`[PERF] 支付耗时: ${duration}ms`);
    return result;
  }
}

// 使用:动态组合功能
let payment = new BasicPayment();
payment = new LoggingDecorator(payment);
payment = new MonitoringDecorator(payment);

payment.pay(100);
// 输出:
// [LOG] 支付开始: 100
// 执行核心支付逻辑: 100
// [LOG] 支付结束,结果: true
// [PERF] 支付耗时: 1ms

注意:装饰器可以嵌套,但要避免过度嵌套导致“套娃地狱”,增加调试难度。

与代理模式区别

  • 装饰器:目的就是添加新功能(赋能)。
  • 代理:目的主要是控制访问(保护/拦截)。
10. 外观模式 (Facade)

定义:为子系统中的一组接口提供一个统一的、更高层的接口,使子系统更容易使用。

核心整合复杂流程,提供一个“一键操作”入口。

场景:订单下单流程,涉及多个子系统调用。

// 复杂的子系统
class InventoryService {
  checkStock(productId) { /* 检查库存 */ }
  reduceStock(productId) { /* 扣减库存 */ }
}
class PaymentService {
  processPayment(orderId, amount) { /* 处理支付 */ }
}
class ShippingService {
  scheduleDelivery(orderId, address) { /* 安排物流 */ }
}
class NotificationService {
  sendConfirm(orderId, email) { /* 发送确认邮件 */ }
}

// 客户端需要了解并调用所有细节
const inventory = new InventoryService();
const payment = new PaymentService();
const shipping = new ShippingService();
const notification = new NotificationService();

function placeOrderOld(productId, address, email) {
  if (!inventory.checkStock(productId)) throw new Error('库存不足');
  inventory.reduceStock(productId);
  const orderId = `ORDER_${Date.now()}`;
  payment.processPayment(orderId, 100);
  shipping.scheduleDelivery(orderId, address);
  notification.sendConfirm(orderId, email);
  return orderId;
}

外观模式:提供一个简化的接口。

// 外观类
class OrderFacade {
  constructor() {
    this.inventory = new InventoryService();
    this.payment = new PaymentService();
    this.shipping = new ShippingService();
    this.notification = new NotificationService();
  }
  placeOrder(productId, address, email) {
    console.log('=== 开始下单流程 ===');
    if (!this.inventory.checkStock(productId)) throw new Error('库存不足');
    this.inventory.reduceStock(productId);
    const orderId = `ORDER_${Date.now()}`;
    this.payment.processPayment(orderId, 100);
    this.shipping.scheduleDelivery(orderId, address);
    this.notification.sendConfirm(orderId, email);
    console.log('=== 下单流程结束 ===');
    return orderId;
  }
}

// 客户端调用变得极其简单
const orderFacade = new OrderFacade();
const orderId = orderFacade.placeOrder('PROD_001', '北京', 'user@example.com');
11. 享元模式 (Flyweight)

定义:运用共享技术来有效支持大量细粒度对象的复用。

核心:分离对象的内部状态(不变,可共享)和外部状态(变化,由客户端传入)。

场景:多人网络游戏中,大量玩家可能穿戴相同的武器和服装。与其为每个玩家实例化一套装备对象,不如共享。

// 享元对象:武器(内部状态,不变)
class Weapon {
  constructor(name, texture, damage) {
    this.name = name; // 内部状态
    this.texture = texture;
    this.damage = damage;
  }
  use(playerName) { // playerName是外部状态
    console.log(`${playerName} 使用了 ${this.name}, 造成 ${this.damage} 点伤害。`);
  }
}

// 享元工厂:缓存和共享享元对象
class WeaponFactory {
  constructor() {
    this.weapons = new Map();
  }
  getWeapon(name) {
    if (!this.weapons.has(name)) {
      // 模拟从资源库加载
      let weapon;
      if (name === 'golden_cudgel') {
        weapon = new Weapon('金箍棒', 'gold_texture.png', 999);
      } else if (name === 'wooden_sword') {
        weapon = new Weapon('木剑', 'wood_texture.png', 10);
      }
      this.weapons.set(name, weapon);
      console.log(`创建新武器: ${name}`);
    }
    return this.weapons.get(name);
  }
}

// 客户端(玩家)
class Player {
  constructor(name, weaponName, weaponFactory) {
    this.name = name; // 外部状态
    this.weapon = weaponFactory.getWeapon(weaponName); // 获取共享的内部状态
  }
  attack() {
    this.weapon.use(this.name); // 使用武器时传入外部状态
  }
}

// 使用
const factory = new WeaponFactory();
const player1 = new Player('孙悟空', 'golden_cudgel', factory);
const player2 = new Player('六耳猕猴', 'golden_cudgel', factory); // 复用同一个金箍棒对象
const player3 = new Player('新手', 'wooden_sword', factory);

player1.attack(); // 孙悟空 使用了 金箍棒, 造成 999 点伤害。
player2.attack(); // 六耳猕猴 使用了 金箍棒, 造成 999 点伤害。
console.log(player1.weapon === player2.weapon); // true,是同一个对象!

优点:极大减少内存中相似对象的数量,提升性能。

12. 代理模式 (Proxy)

定义:为其他对象提供一种代理以控制对这个对象的访问。

核心访问控制。如同明星的经纪人。

场景:过滤给明星的剧本。

// 主体接口
class Star {
  receiveScript(script) { throw new Error('必须实现'); }
}

// 真实主体
class RealStar extends Star {
  constructor(name) {
    super();
    this.name = name;
  }
  receiveScript(script) {
    console.log(`${this.name} 收到了剧本:《${script.title}》`);
    // 假设明星自己也会看内容
    if (script.content.includes('烂片')) {
      console.log(`${this.name} 说:这是烂片,不接!`);
      return false;
    }
    return true;
  }
}

// 代理
class Agent extends Star {
  constructor(star) {
    super();
    this.star = star;
    this.blacklist = ['恐怖片', '低俗喜剧']; // 代理的过滤逻辑
  }
  receiveScript(script) {
    console.log(`经纪人收到剧本:《${script.title}》,开始审核...`);
    // 1. 代理首先过滤
    if (this.blacklist.includes(script.genre)) {
      console.log(`经纪人:${script.genre}类型不符合${this.star.name}形象,拒绝。`);
      return false;
    }
    if (script.budget < 1000) {
      console.log(`经纪人:预算太低,拒绝。`);
      return false;
    }
    // 2. 通过初审,再交给明星本人决定
    console.log(`经纪人:初步通过,转交给${this.star.name}。`);
    return this.star.receiveScript(script);
  }
}

// 使用
const superstar = new RealStar('刘德华');
const agent = new Agent(superstar);

const script1 = { title: '低俗故事', genre: '低俗喜剧', budget: 5000 };
const script2 = { title: '大制作', genre: '剧情', budget: 2000, content: '好剧本' };
const script3 = { title: '粗制滥造', genre: '动作', budget: 2000, content: '烂片' };

console.log(agent.receiveScript(script1)); // 经纪人过滤掉
console.log(agent.receiveScript(script2)); // 经纪人通过,明星也通过
console.log(agent.receiveScript(script3)); // 经纪人通过,明星自己拒绝

与装饰器模式区别(论心不论迹):

  • 装饰器:一开始就想增强对象功能。
  • 代理:一开始就想控制对对象的访问,附加功能是控制的手段。

三、行为型模式

解决类与对象之间如何交互、如何分配职责的问题。共11种:职责链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者。

13. 职责链模式 (Chain of Responsibility)

定义:将请求的发送者和接收者解耦,使多个对象都有机会处理该请求。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。

场景:Bug工单在研发部门的流转(前端组 -> 后端组 -> 测试组)。

// 处理器抽象类
class BugHandler {
  constructor() {
    this.nextHandler = null;
  }
  setNext(handler) {
    this.nextHandler = handler;
    return handler; // 支持链式调用
  }
  handle(bug) {
    if (this.nextHandler) {
      return this.nextHandler.handle(bug);
    }
    console.log(`没有团队能处理这个Bug: ${bug.description}`);
    return null;
  }
}

// 具体处理器
class FrontendTeam extends BugHandler {
  handle(bug) {
    if (bug.type === 'UI' || bug.type === 'JavaScript') {
      console.log(`前端组处理了Bug: ${bug.description}`);
      return `前端修复-${bug.id}`;
    }
    console.log(`前端组无法处理,转交下一环节`);
    return super.handle(bug); // 传递给链上的下一个
  }
}
class BackendTeam extends BugHandler {
  handle(bug) {
    if (bug.type === 'API' || bug.type === 'Database') {
      console.log(`后端组处理了Bug: ${bug.description}`);
      return `后端修复-${bug.id}`;
    }
    console.log(`后端组无法处理,转交下一环节`);
    return super.handle(bug);
  }
}
class TestTeam extends BugHandler {
  handle(bug) {
    if (bug.type === 'Performance' || bug.type === 'Compatibility') {
      console.log(`测试组处理了Bug: ${bug.description}`);
      return `测试验证-${bug.id}`;
    }
    console.log(`测试组无法处理,转交下一环节`);
    return super.handle(bug);
  }
}

// 构建职责链
const frontend = new FrontendTeam();
const backend = new BackendTeam();
const test = new TestTeam();
frontend.setNext(backend).setNext(test); // 前端 -> 后端 -> 测试

// 提交Bug
const bugs = [
  { id: 1, type: 'JavaScript', description: '页面点击无效' },
  { id: 2, type: 'Database', description: '数据查询超时' },
  { id: 3, type: 'Performance', description: '首页加载慢' },
  { id: 4, type: 'Hardware', description: '服务器宕机' }, // 无人能处理
];

bugs.forEach(bug => {
  console.log(`\n提交Bug: ${bug.description}`);
  const result = frontend.handle(bug);
  console.log(`处理结果: ${result}`);
});
14. 命令模式 (Command)

定义:将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,支持请求的排队、记录日志、撤销等操作。

核心:将 “动作” 及其相关数据封装成独立对象。

场景:支持撤销的订单操作。

// 命令接口
class Command {
  execute() { throw new Error('必须实现'); }
  undo() { throw new Error('必须实现'); }
}

// 接收者:执行实际业务的对象
class OrderService {
  createOrder(data) {
    console.log(`创建订单: ${JSON.stringify(data)}`);
    return `ORDER_${Date.now()}`;
  }
  cancelOrder(orderId) {
    console.log(`取消订单: ${orderId}`);
  }
}

// 具体命令
class CreateOrderCommand extends Command {
  constructor(orderService, orderData) {
    super();
    this.orderService = orderService;
    this.orderData = orderData;
    this.orderId = null; // 保存执行结果,用于撤销
  }
  execute() {
    this.orderId = this.orderService.createOrder(this.orderData);
    return this.orderId;
  }
  undo() {
    if (this.orderId) {
      this.orderService.cancelOrder(this.orderId);
      this.orderId = null;
    }
  }
}

class CancelOrderCommand extends Command {
  constructor(orderService, orderId) {
    super();
    this.orderService = orderService;
    this.orderId = orderId;
    this.wasExecuted = false; // 记录状态,防止重复撤销
  }
  execute() {
    this.orderService.cancelOrder(this.orderId);
    this.wasExecuted = true;
  }
  undo() {
    if (this.wasExecuted) {
      console.log(`撤销取消操作,恢复订单: ${this.orderId} (模拟)`);
      this.wasExecuted = false;
    }
  }
}

// 调用者/控制器
class CommandController {
  constructor() {
    this.history = []; // 命令历史,用于撤销
  }
  executeCommand(command) {
    const result = command.execute();
    this.history.push(command); // 记录
    return result;
  }
  undoLastCommand() {
    if (this.history.length > 0) {
      const lastCommand = this.history.pop();
      lastCommand.undo();
    }
  }
}

// 使用
const orderService = new OrderService();
const controller = new CommandController();

const cmd1 = new CreateOrderCommand(orderService, { product: '手机', amount: 1 });
const orderId = controller.executeCommand(cmd1); // 执行创建

const cmd2 = new CancelOrderCommand(orderService, orderId);
controller.executeCommand(cmd2); // 执行取消

console.log('\n--- 开始撤销 ---');
controller.undoLastCommand(); // 撤销“取消”
controller.undoLastCommand(); // 撤销“创建”

强大之处:命令对象可以像普通对象一样被存储、传递、排队(如异步任务队列)、记录日志(实现持久化和重试),并支持撤销/重做。

15. 解释器模式 (Interpreter)

定义:给定一个语言(或文法),定义它的文法的一种表示,并定义一个解释器,使用该表示来解释语言中的句子。

场景:动态、可配置的业务规则引擎(如优惠券规则)。规则如:“VIP用户 且 订单金额 > 1000” 打八折。

传统硬编码问题:if-else爆炸,难以维护和动态修改。

解释器模式思路:将规则中的每个元素(变量、常量、运算符)都变成对象,然后组合成一棵“抽象语法树”来解释。

// 抽象表达式
class RuleExpression {
  interpret(context) { throw new Error('必须实现'); }
}

// 终结符表达式:变量(从上下文取值)
class VariableExpression extends RuleExpression {
  constructor(variableName) {
    super();
    this.variableName = variableName;
  }
  interpret(context) {
    return context.lookup(this.variableName);
  }
}
// 终结符表达式:常量
class ConstantExpression extends RuleExpression {
  constructor(value) {
    super();
    this.value = value;
  }
  interpret(context) {
    return this.value;
  }
}

// 非终结符表达式:比较操作 >
class GreaterThanExpression extends RuleExpression {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }
  interpret(context) {
    return this.left.interpret(context) > this.right.interpret(context);
  }
}
// 非终结符表达式:逻辑操作 &&
class AndExpression extends RuleExpression {
  constructor(left, right) {
    super();
    this.left = left;
    this.right = right;
  }
  interpret(context) {
    return this.left.interpret(context) && this.right.interpret(context);
  }
}

// 上下文:存储变量和提供查询
class Context {
  constructor() {
    this.variables = new Map();
  }
  setVariable(name, value) {
    this.variables.set(name, value);
  }
  lookup(name) {
    if (!this.variables.has(name)) {
      throw new Error(`变量 ${name} 未定义`);
    }
    return this.variables.get(name);
  }
}

// 构建规则:isVIP && amount > 1000
const isVIP = new VariableExpression('isVIP');
const amount = new VariableExpression('amount');
const rule = new AndExpression(
  isVIP,
  new GreaterThanExpression(amount, new ConstantExpression(1000))
);

// 使用
const context = new Context();
context.setVariable('isVIP', true);
context.setVariable('amount', 1500);

console.log(rule.interpret(context)); // true: VIP用户,金额1500>1000

context.setVariable('amount', 800);
console.log(rule.interpret(context)); // false: VIP用户,但金额800不大于1000

进一步:可以将规则与具体的优惠动作(命令模式)绑定,形成完整的动态规则引擎。解释器模式适合处理需要频繁变化、可配置的复杂逻辑,但实现本身较复杂。

16. 迭代器模式 (Iterator)

定义:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。

核心:将遍历行为从聚合对象中分离出来。

JavaScript/ES6 已原生支持Symbol.iterator),但理解其思想有助于设计。

// 迭代器接口
class Iterator {
  hasNext() { throw new Error('必须实现'); }
  next() { throw new Error('必须实现'); }
}

// 具体迭代器
class ArrayIterator extends Iterator {
  constructor(collection) {
    super();
    this.collection = collection;
    this.index = 0;
  }
  hasNext() {
    return this.index < this.collection.length;
  }
  next() {
    if (this.hasNext()) {
      return this.collection[this.index++];
    }
    return null;
  }
}

// 聚合对象接口
class Aggregate {
  createIterator() { throw new Error('必须实现'); }
}

// 具体聚合对象
class NumberCollection extends Aggregate {
  constructor() {
    super();
    this.numbers = [];
  }
  add(number) {
    this.numbers.push(number);
  }
  createIterator() {
    return new ArrayIterator(this.numbers);
  }
}

// 使用
const collection = new NumberCollection();
collection.add(1);
collection.add(2);
collection.add(3);

const iterator = collection.createIterator();
while (iterator.hasNext()) {
  console.log(iterator.next()); // 1, 2, 3
}

优点

  1. 支持多种遍历方式:可轻松创建反向、跳跃等迭代器。
  2. 简化聚合对象接口:聚合对象只需提供创建迭代器的方法。
  3. 解耦:客户端代码与聚合对象的数据结构解耦。

与相似模式区别

  • 迭代器:目的是分离遍历逻辑,便于变化。
  • 装饰器、适配器、代理:目的各不相同(添加功能、兼容接口、控制访问)。
17. 中介者模式 (Mediator)

定义:用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用,从而使其耦合松散。

场景:商城模块间复杂的网状调用(购物车、商品、订单、支付模块相互调用)。
问题:模块间直接依赖,形成复杂的网状结构,难以维护。
网状依赖问题

中介者模式:引入一个“中介者”协调所有交互。

// 中介者接口
class Mediator {
  notify(sender, event, data) { throw new Error('必须实现'); }
}

// 具体中介者
class ShoppingMediator extends Mediator {
  constructor(cart, product, order, payment) {
    super();
    this.cart = cart;
    this.product = product;
    this.order = order;
    this.payment = payment;
    // 设置模块的中介者为自己
    this.cart.setMediator(this);
    this.product.setMediator(this);
    this.order.setMediator(this);
    this.payment.setMediator(this);
  }
  notify(sender, event, data) {
    switch (event) {
      case 'ADD_TO_CART':
        this.cart.addItem(data.productId);
        break;
      case 'CHECKOUT':
        const orderId = this.order.createOrder(data.cartItems);
        this.payment.process(orderId, data.amount);
        break;
      case 'ORDER_CANCELLED':
        this.payment.refund(data.orderId);
        break;
      // ... 处理其他事件
    }
  }
}

// 基础组件类
class BaseComponent {
  constructor() {
    this.mediator = null;
  }
  setMediator(mediator) {
    this.mediator = mediator;
  }
}

// 具体组件(简化版)
class CartModule extends BaseComponent {
  addItem(productId) {
    console.log(`购物车添加商品: ${productId}`);
    // 添加成功后,可能通过中介者通知其他模块,例如更新库存
    this.mediator.notify(this, 'ITEM_ADDED', { productId });
  }
  checkout() {
    const items = ['PROD1', 'PROD2'];
    console.log('购物车结算');
    this.mediator.notify(this, 'CHECKOUT', { cartItems: items, amount: 200 });
  }
}
// ... 其他模块类似定义

// 使用
const cart = new CartModule();
const product = new ProductModule();
const order = new OrderModule();
const payment = new PaymentModule();

const mediator = new ShoppingMediator(cart, product, order, payment);

// 用户操作:添加商品到购物车
product.addToCart('PROD100'); // 内部会调用 mediator.notify(..., 'ADD_TO_CART', ...)
// 用户操作:结算
cart.checkout(); // 内部会通过中介者协调订单创建和支付

优点:将多对多的交互转化为一对多(所有模块只与中介者交互),降低耦合。
缺点:中介者自身可能变得过于复杂,成为新的“上帝对象”。

18. 备忘录模式 (Memento)

定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可将该对象恢复到原先保存的状态。

核心状态快照与恢复。常用于撤销操作。

三要素

  1. 原发器 (Originator):需要保存状态的对象。
  2. 备忘录 (Memento):存储原发器内部状态的对象。
  3. 管理者 (Caretaker):负责保存和管理备忘录。
    
    // 备忘录(存储状态)
    class CartMemento {
    constructor(items) {
    this.items = JSON.parse(JSON.stringify(items)); // 深拷贝状态
    this.savedAt = new Date();
    }
    }

// 原发器
class ShoppingCart {
constructor() {
this.items = [];
}
addItem(item) {
this.items.push(item);
console.log(添加商品: ${item},当前购物车:, this.items);
}
// 创建备忘录(保存状态)
save() {
return new CartMemento(this.items);
}
// 从备忘录恢复状态
restore(memento) {
this.items = memento.items;
console.log(恢复到 ${memento.savedAt} 的状态:, this.items);
}
}

// 管理者
class CartHistory {
constructor() {
this.history = [];
}
push(memento) {
this.history.push(memento);
}
pop() {
return this.history.pop();
}
}

// 使用
const cart = new ShoppingCart();
const history = new CartHistory();

cart.addItem('苹果'); // 状态1
history.push(cart.save());

cart.addItem('香蕉'); // 状态2
history.push(cart.save());

cart.addItem('橙子'); // 状态3
console.log('当前购物车:', cart.items);

// 撤销一步
const lastState = history.pop();
cart.restore(lastState); // 恢复到状态2
// 再撤销一步
const prevState = history.pop();
cart.restore(prevState); // 恢复到状态1


**结合其他模式**:备忘录的保存和恢复操作可以封装在**命令模式**的命令对象中(实现可撤销的命令),或者由**外观模式**提供一个统一的“保存点”管理接口。

#### 19. 观察者模式 (Observer)
**定义**:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

**非常经典和常用**,如事件监听系统。
```javascript
// 主题(被观察者)
class Subject {
  constructor() {
    this.observers = [];
  }
  addObserver(observer) {
    this.observers.push(observer);
  }
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index > -1) {
      this.observers.splice(index, 1);
    }
  }
  notify(data) {
    for (const observer of this.observers) {
      observer.update(data);
    }
  }
}

// 观察者
class Observer {
  update(data) {
    console.log(`收到通知: ${data}`);
  }
}

// 具体主题
class Cart extends Subject {
  addItem(item) {
    // ... 添加逻辑
    console.log(`商品 ${item} 加入购物车`);
    this.notify(item); // 状态变化,通知所有观察者
  }
}

// 具体观察者
class InventoryObserver extends Observer {
  update(item) {
    console.log(`[库存系统] 商品 ${item} 被购买,需要检查库存。`);
  }
}
class RecommendationObserver extends Observer {
  update(item) {
    console.log(`[推荐系统] 用户购买了 ${item},更新推荐列表。`);
  }
}

// 使用
const cart = new Cart();
const inventory = new InventoryObserver();
const recommendation = new RecommendationObserver();

cart.addObserver(inventory);
cart.addObserver(recommendation);

cart.addItem('《设计模式》');
// 输出:
// 商品 《设计模式》 加入购物车
// [库存系统] 商品 《设计模式》 被购买,需要检查库存。
// [推荐系统] 用户购买了 《设计模式》,更新推荐列表。

优势:实现了发布-订阅机制,让主题和观察者松耦合。主题无需知道观察者的具体细节,新增观察者也无须修改主题代码。

20. 状态模式 (State)

定义:允许一个对象在其内部状态改变时改变它的行为。

场景:订单状态(待支付、已支付、已发货),不同状态下的操作(支付、取消)行为不同。
传统问题:大量if-elseswitch-case判断状态。

// 不好的方式
class Order {
  constructor() {
    this.state = 'PENDING';
  }
  pay() {
    if (this.state === 'PENDING') {
      console.log('执行支付...');
      this.state = 'PAID';
    } else if (this.state === 'PAID') {
      console.log('订单已支付,无需重复支付。');
    } else if (this.state === 'SHIPPED') {
      console.log('订单已发货,无法支付。');
    }
    // ... 更多状态
  }
  cancel() {
    if (this.state === 'PENDING') {
      console.log('订单取消。');
      this.state = 'CANCELLED';
    } else if (this.state === 'PAID') {
      console.log('已支付订单取消,需要退款。');
      this.state = 'REFUNDING';
    } else if (this.state === 'SHIPPED') {
      console.log('已发货,无法取消。');
    }
    // ...
  }
}

状态模式:将每个状态及其行为封装成独立的类。

// 状态接口
class OrderState {
  pay(order) { throw new Error('必须实现'); }
  cancel(order) { throw new Error('必须实现'); }
}

// 具体状态
class PendingState extends OrderState {
  pay(order) {
    console.log('支付成功。');
    order.setState(new PaidState());
  }
  cancel(order) {
    console.log('订单已取消。');
    order.setState(new CancelledState());
  }
}
class PaidState extends OrderState {
  pay(order) {
    console.log('订单已支付,无需重复支付。');
  }
  cancel(order) {
    console.log('取消已支付订单,发起退款。');
    order.setState(new RefundingState());
  }
}
class ShippedState extends OrderState {
  pay(order) {
    console.log('订单已发货,无法支付。');
  }
  cancel(order) {
    console.log('订单已发货,无法取消。');
  }
}
// ... 其他状态类

// 上下文(订单)
class Order {
  constructor() {
    this.setState(new PendingState());
  }
  setState(state) {
    this.state = state;
    console.log(`订单状态变为: ${state.constructor.name}`);
  }
  pay() {
    this.state.pay(this);
  }
  cancel() {
    this.state.cancel(this);
  }
}

// 使用
const order = new Order(); // 初始状态:Pending
order.pay(); // 支付成功 -> Paid
order.pay(); // 订单已支付,无需重复支付。
order.cancel(); // 取消已支付订单,发起退款 -> Refunding

优点:消除了庞大的条件分支语句,将状态相关的行为局部化到对应的状态类中,符合“开闭原则”,新增状态只需增加新类。

21. 策略模式 (Strategy)

定义:定义一系列算法,将它们一个个封装起来,并且使它们可以相互替换。

场景:多种价格计算策略(普通折扣、满减、会员价)。
传统问题if-else选择算法。

// 不好的方式
function calculatePrice(originalPrice, strategyType) {
  if (strategyType === 'NORMAL_DISCOUNT') {
    return originalPrice * 0.9;
  } else if (strategyType === 'FULL_REDUCTION') {
    if (originalPrice >= 200) return originalPrice - 50;
    return originalPrice;
  } else if (strategyType === 'VIP_PRICE') {
    return originalPrice * 0.8;
  }
  return originalPrice;
}

策略模式:将每种算法封装成独立的策略类。

// 策略接口
class PricingStrategy {
  calculate(price) { throw new Error('必须实现'); }
}

// 具体策略
class NormalDiscountStrategy extends PricingStrategy {
  calculate(price) {
    return price * 0.9;
  }
}
class FullReductionStrategy extends PricingStrategy {
  calculate(price) {
    if (price >= 200) {
      return price - 50;
    }
    return price;
  }
}
class VIPStrategy extends PricingStrategy {
  calculate(price) {
    return price * 0.8;
  }
}

// 上下文
class Order {
  constructor(strategy) {
    this.strategy = strategy;
    this.price = 0;
  }
  setPrice(price) {
    this.price = price;
  }
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  calculateFinalPrice() {
    return this.strategy.calculate(this.price);
  }
}

// 使用
const order = new Order(new NormalDiscountStrategy());
order.setPrice(100);
console.log('普通折扣:', order.calculateFinalPrice()); // 90

order.setStrategy(new FullReductionStrategy());
console.log('满减:', order.calculateFinalPrice()); // 100 (未满200)

order.setPrice(250);
console.log('满减:', order.calculateFinalPrice()); // 200 (250-50)

order.setStrategy(new VIPStrategy());
console.log('VIP价:', order.calculateFinalPrice()); // 200 (250*0.8)

核心逻辑分离与动态替换。客户端可以根据需要灵活选择或切换策略。

与状态模式区别

  • 策略模式:客户端主动选择或更换算法,策略之间通常独立,不自动转换。
  • 状态模式:状态转换由上下文内部行为触发,状态间存在流转关系。
22. 模板方法模式 (Template Method)

定义:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

与工厂方法模式神似,但关注点不同。

// 抽象类定义模板
class DataExporter {
  // 模板方法:定义算法骨架(final,不应被子类重写)
  export() {
    this.openConnection();
    const data = this.fetchData();
    const processedData = this.processData(data);
    this.saveToFile(processedData);
    this.closeConnection();
    console.log('数据导出完成。');
  }
  // 具体步骤,子类可能需重写
  openConnection() {
    console.log('打开数据库连接...');
  }
  fetchData() {
    throw new Error('子类必须实现 fetchData');
  }
  processData(data) {
    // 默认处理:直接返回
    console.log('默认数据处理...');
    return data;
  }
  saveToFile(data) {
    console.log(`保存数据到文件: ${JSON.stringify(data).slice(0, 50)}...`);
  }
  closeConnection() {
    console.log('关闭数据库连接。');
  }
}

// 具体子类
class UserExporter extends DataExporter {
  fetchData() {
    console.log('从Users表获取数据...');
    return [{ id: 1, name: '张三' }, { id: 2, name: '李四' }];
  }
  // 可以重写processData以提供特殊处理
  processData(data) {
    console.log('处理用户数据:加密手机号...');
    return data.map(user => ({ ...user, phone: '***' }));
  }
}
class ProductExporter extends DataExporter {
  fetchData() {
    console.log('从Products表获取数据...');
    return [{ id: 101, name: '手机', price: 1999 }];
  }
}

// 使用
const userExporter = new UserExporter();
userExporter.export();
// 输出:
// 打开数据库连接...
// 从Users表获取数据...
// 处理用户数据:加密手机号...
// 保存数据到文件: [{"id":1,"name":"张三","phone":"***"},{"id":2,...
// 关闭数据库连接。
// 数据导出完成。

const productExporter = new ProductExporter();
productExporter.export();

与工厂方法模式区别

  • 工厂方法:重点在子类创建对象,父类提供一个框架,但核心是“创建”。
  • 模板方法:重点在父类定义流程骨架,子类只是填充或修改其中某些步骤。父类是“老板”,规定了工作流程。
23. 访问者模式 (Visitor)

定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。

核心将数据结构和作用于结构上的操作解耦

场景:对不同类型的图形元素(圆形、矩形)执行不同的操作(计算面积、计算周长)。
传统紧耦合方式:操作写在每个图形类里,违反开闭原则。

// 不好的方式:操作与数据结构耦合
class Circle {
  constructor(radius) { this.radius = radius; }
  getArea() { return Math.PI * this.radius * this.radius; }
  getPerimeter() { return 2 * Math.PI * this.radius; }
  // 新增操作(如绘制)需要修改类
}

访问者模式

// 元素接口(接受访问者)
class Shape {
  accept(visitor) { throw new Error('必须实现'); }
}

// 具体元素
class Circle extends Shape {
  constructor(radius) {
    super();
    this.radius = radius;
  }
  accept(visitor) {
    visitor.visitCircle(this); // 让访问者来处理自己
  }
}
class Rectangle extends Shape {
  constructor(width, height) {
    super();
    this.width = width;
    this.height = height;
  }
  accept(visitor) {
    visitor.visitRectangle(this);
  }
}

// 访问者接口
class ShapeVisitor {
  visitCircle(circle) { throw new Error('必须实现'); }
  visitRectangle(rectangle) { throw new Error('必须实现'); }
}

// 具体访问者:面积计算
class AreaVisitor extends ShapeVisitor {
  visitCircle(circle) {
    const area = Math.PI * circle.radius * circle.radius;
    console.log(`圆形面积: ${area.toFixed(2)}`);
    return area;
  }
  visitRectangle(rectangle) {
    const area = rectangle.width * rectangle.height;
    console.log(`矩形面积: ${area}`);
    return area;
  }
}
// 具体访问者:周长计算
class PerimeterVisitor extends ShapeVisitor {
  visitCircle(circle) {
    const perimeter = 2 * Math.PI * circle.radius;
    console.log(`圆形周长: ${perimeter.toFixed(2)}`);
    return perimeter;
  }
  visitRectangle(rectangle) {
    const perimeter = 2 * (rectangle.width + rectangle.height);
    console.log(`矩形周长: ${perimeter}`);
    return perimeter;
  }
}
// 新增访问者(如绘制)非常容易,无需修改Shape类
class DrawVisitor extends ShapeVisitor {
  visitCircle(circle) { console.log(`绘制半径为 ${circle.radius} 的圆`); }
  visitRectangle(rect) { console.log(`绘制 ${rect.width}x${rect.height} 的矩形`); }
}

// 使用
const shapes = [
  new Circle(5),
  new Rectangle(4, 6),
  new Circle(3)
];

const areaVisitor = new AreaVisitor();
const perimeterVisitor = new PerimeterVisitor();
const drawVisitor = new DrawVisitor();

console.log('=== 计算面积 ===');
shapes.forEach(shape => shape.accept(areaVisitor));

console.log('\n=== 计算周长 ===');
shapes.forEach(shape => shape.accept(perimeterVisitor));

console.log('\n=== 绘制图形 ===');
shapes.forEach(shape => shape.accept(drawVisitor));

工作机制(双分派)shape.accept(visitor) 调用是第一次分派(根据shape类型)。在accept内部,visitor.visitCircle(this)是第二次分派(根据visitor类型)。通过两次动态绑定,实现了操作与数据的解耦。

优点:新增操作容易,符合开闭原则。
缺点:增加新的元素类(如新增Triangle)困难,需要修改所有访问者接口和类。因此适用于元素类稳定,但操作经常变化的场景。

与策略模式区别

  • 策略模式:通常只有一个上下文,策略封装一个算法。
  • 访问者模式:作用于对象结构(多个不同类型的元素),为每个元素类型定义操作。

总结

终于将23种设计模式梳理完毕。它们之所以相似却各有其名,是因为每种模式都是针对特定问题场景的解决方案。模式的表现形式(如“统一接口”)可能相同,但其设计意图和适用场景决定了它是哪一种模式。

掌握设计模式,不仅是记住定义和结构,更是理解其背后的“为什么”——为什么要解耦?为什么要组合?理解了这些思想,才能在复杂的软件工程实践中灵活运用,构建出健壮、可维护的系统,而非生搬硬套。

(完)




上一篇:Linux驱动开发实战指南:sysfs接口创建与设备模型全流程解析
下一篇:技术升级的困境:企业坚守JDK8而非JDK25的七大现实考量
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 18:59 , Processed in 0.257266 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表