Skip to content

多态性:一个接口多种实现的艺术

多态是什么

想象你走进一家智能家居展厅, 墙上有一排开关。每个开关看起来都一样, 都有一个"开/关"按钮, 但它们控制的设备完全不同:一个控制灯光, 一个控制空调, 一个控制音响, 还有一个控制窗帘。你不需要学习四种不同的操作方式, 只需要知道"按下开关", 具体的设备会做出相应的响应。

这就是多态(Polymorphism)的精髓:"一个接口, 多种实现"。虽然所有开关都提供相同的接口(按钮), 但每个开关连接到不同的设备, 执行不同的操作。在编程中, 多态让我们能够用统一的方式处理不同类型的对象, 每个对象会根据自己的具体类型做出恰当的响应。

多态的基本原理

多态这个词来自希腊语, 意思是"多种形态"。在面向对象编程中, 多态允许我们:

  1. 统一接口: 定义通用的方法名称
  2. 不同实现: 各个子类提供自己独特的实现
  3. 动态调用: 运行时根据对象的实际类型调用相应的方法

让我们通过一个简单的例子理解多态:

javascript
// 基类定义通用接口
class Animal {
  constructor(name) {
    this.name = name;
  }

  // 通用方法 - 子类会重写
  makeSound() {
    return "Some generic animal sound";
  }

  introduce() {
    return `I am ${this.name} and I say: ${this.makeSound()}`;
  }
}

// 不同的子类, 不同的实现
class Dog extends Animal {
  makeSound() {
    return "Woof! Woof!";
  }
}

class Cat extends Animal {
  makeSound() {
    return "Meow~";
  }
}

class Cow extends Animal {
  makeSound() {
    return "Moo!";
  }
}

class Duck extends Animal {
  makeSound() {
    return "Quack! Quack!";
  }
}

// 多态的威力:统一的处理方式
function performConcert(animals) {
  console.log("🎵 动物音乐会开始! 🎵\n");

  for (const animal of animals) {
    // 同样是调用 makeSound(), 但每个动物发出不同的声音
    console.log(animal.introduce());
  }

  console.log("\n👏 音乐会结束!");
}

// 创建不同类型的动物
const performers = [
  new Dog("Max"),
  new Cat("Whiskers"),
  new Cow("Bessie"),
  new Duck("Donald"),
];

// 统一处理, 每个动物自动使用自己的实现
performConcert(performers);
// 🎵 动物音乐会开始! 🎵
//
// I am Max and I say: Woof! Woof!
// I am Whiskers and I say: Meow~
// I am Bessie and I say: Moo!
// I am Donald and I say: Quack! Quack!
//
// 👏 音乐会结束!

关键点在于:performConcert 函数不需要知道每个动物的具体类型, 它只需要知道"所有动物都有 makeSound() 方法"。每个动物对象会自动使用自己类中定义的 makeSound() 实现。

方法重写(Method Overriding)

方法重写是实现多态的主要方式。子类可以提供与父类同名方法的不同实现:

javascript
class Shape {
  constructor(name, color) {
    this.name = name;
    this.color = color;
  }

  // 通用方法 - 待子类重写
  getArea() {
    throw new Error(`${this.constructor.name} 必须实现 getArea() 方法`);
  }

  getPerimeter() {
    throw new Error(`${this.constructor.name} 必须实现 getPerimeter() 方法`);
  }

  // 通用方法 - 所有子类共享
  describe() {
    return `这是一个${this.color}的${this.name}`;
  }

  // 使用多态方法的通用方法
  getInfo() {
    return {
      name: this.name,
      color: this.color,
      area: this.getArea(),
      perimeter: this.getPerimeter(),
      description: this.describe(),
    };
  }
}

class Circle extends Shape {
  constructor(radius, color = "红色") {
    super("圆形", color);
    this.radius = radius;
  }

  // 重写 getArea - 圆形特定的计算
  getArea() {
    return Math.PI * this.radius ** 2;
  }

  // 重写 getPerimeter - 圆形特定的计算
  getPerimeter() {
    return 2 * Math.PI * this.radius;
  }

  // 重写 describe - 添加圆形特定信息
  describe() {
    return `${super.describe()}, 半径为 ${this.radius}`;
  }
}

class Rectangle extends Shape {
  constructor(width, height, color = "蓝色") {
    super("矩形", color);
    this.width = width;
    this.height = height;
  }

  getArea() {
    return this.width * this.height;
  }

  getPerimeter() {
    return 2 * (this.width + this.height);
  }

  describe() {
    return `${super.describe()}, 宽 ${this.width}, 高 ${this.height}`;
  }

  // 矩形特有的方法
  isSquare() {
    return this.width === this.height;
  }
}

class Triangle extends Shape {
  constructor(a, b, c, color = "绿色") {
    super("三角形", color);
    this.a = a;
    this.b = b;
    this.c = c;
  }

  getArea() {
    // 使用海伦公式
    const s = this.getPerimeter() / 2;
    return Math.sqrt(s * (s - this.a) * (s - this.b) * (s - this.c));
  }

  getPerimeter() {
    return this.a + this.b + this.c;
  }

  describe() {
    return `${super.describe()}, 边长分别为 ${this.a},  ${this.b},  ${this.c}`;
  }
}

// 多态应用:计算多个图形的总面积
function calculateTotalArea(shapes) {
  console.log("📐 图形面积统计 📐\n");

  let totalArea = 0;

  for (const shape of shapes) {
    const info = shape.getInfo();
    console.log(`${info.description}`);
    console.log(`  面积: ${info.area.toFixed(2)}`);
    console.log(`  周长: ${info.perimeter.toFixed(2)}\n`);

    totalArea += info.area;
  }

  return totalArea;
}

const shapes = [
  new Circle(5, "红色"),
  new Rectangle(10, 6, "蓝色"),
  new Triangle(3, 4, 5, "绿色"),
  new Circle(3, "黄色"),
];

const total = calculateTotalArea(shapes);
console.log(`📊 总面积: ${total.toFixed(2)}\n`);

接口统一的力量

多态最大的优势在于它允许我们编写通用的代码, 而不需要关心对象的具体类型:

javascript
// 定义支付接口(通过基类)
class PaymentMethod {
  constructor(name) {
    this.name = name;
  }

  // 所有支付方式都必须实现这个方法
  processPayment(amount) {
    throw new Error("processPayment() 必须被实现");
  }

  // 可选的验证方法
  validate() {
    throw new Error("validate() 必须被实现");
  }

  getInfo() {
    return {
      method: this.name,
      type: this.constructor.name,
    };
  }
}

// 各种具体的支付方式
class CreditCard extends PaymentMethod {
  constructor(cardNumber, cvv, expiryDate) {
    super("信用卡");
    this.cardNumber = cardNumber;
    this.cvv = cvv;
    this.expiryDate = expiryDate;
  }

  validate() {
    // 简化的验证逻辑
    if (this.cardNumber.length !== 16) {
      throw new Error("信用卡号必须是16位");
    }

    if (this.cvv.length !== 3) {
      throw new Error("CVV必须是3位");
    }

    const expiry = new Date(this.expiryDate);
    if (expiry < new Date()) {
      throw new Error("信用卡已过期");
    }

    return true;
  }

  processPayment(amount) {
    this.validate();

    console.log(`正在通过信用卡支付 $${amount}...`);
    console.log(`卡号: ****-****-****-${this.cardNumber.slice(-4)}`);

    // 模拟处理延迟
    return {
      success: true,
      method: this.name,
      amount,
      transactionId: `CC-${Date.now()}`,
      timestamp: new Date(),
    };
  }
}

class PayPal extends PaymentMethod {
  constructor(email, password) {
    super("PayPal");
    this.email = email;
    this.password = password;
  }

  validate() {
    if (!this.email.includes("@")) {
      throw new Error("无效的邮箱地址");
    }
    return true;
  }

  processPayment(amount) {
    this.validate();

    console.log(`正在通过PayPal支付 $${amount}...`);
    console.log(`账户: ${this.email}`);

    return {
      success: true,
      method: this.name,
      amount,
      transactionId: `PP-${Date.now()}`,
      timestamp: new Date(),
    };
  }
}

class Bitcoin extends PaymentMethod {
  constructor(walletAddress) {
    super("比特币");
    this.walletAddress = walletAddress;
    this.exchangeRate = 50000; // 简化的汇率
  }

  validate() {
    if (this.walletAddress.length < 26) {
      throw new Error("无效的钱包地址");
    }
    return true;
  }

  processPayment(amount) {
    this.validate();

    const btcAmount = (amount / this.exchangeRate).toFixed(8);

    console.log(`正在通过比特币支付 $${amount}...`);
    console.log(`BTC 金额: ${btcAmount} BTC`);
    console.log(`钱包地址: ${this.walletAddress.slice(0, 8)}...`);

    return {
      success: true,
      method: this.name,
      amount,
      btcAmount,
      transactionId: `BTC-${Date.now()}`,
      timestamp: new Date(),
    };
  }
}

// 订单处理系统 - 多态应用
class OrderProcessor {
  constructor() {
    this.orders = [];
  }

  // 这个方法接受任何支付方式
  processOrder(items, paymentMethod) {
    const total = items.reduce(
      (sum, item) => sum + item.price * item.quantity,
      0
    );

    console.log("\n=== 处理订单 ===");
    console.log(`商品数量: ${items.length}`);
    console.log(`总金额: $${total}`);
    console.log(`支付方式: ${paymentMethod.name}\n`);

    try {
      // 多态!不管是什么支付方式, 都调用 processPayment
      const result = paymentMethod.processPayment(total);

      const order = {
        orderId: `ORD-${Date.now()}`,
        items,
        total,
        payment: result,
        status: "completed",
      };

      this.orders.push(order);

      console.log(`\n✓ 支付成功!`);
      console.log(`订单号: ${order.orderId}`);
      console.log(`交易号: ${result.transactionId}\n`);

      return order;
    } catch (error) {
      console.log(`\n✗ 支付失败: ${error.message}\n`);
      return null;
    }
  }

  getOrderHistory() {
    return this.orders;
  }
}

// 使用示例
const processor = new OrderProcessor();

const items = [
  { name: "Laptop", price: 999, quantity: 1 },
  { name: "Mouse", price: 29, quantity: 2 },
];

// 使用不同的支付方式, 但处理代码完全相同
const creditCard = new CreditCard("1234567890123456", "123", "2025-12");
processor.processOrder(items, creditCard);

const paypal = new PayPal("[email protected]", "password");
processor.processOrder(items, paypal);

const bitcoin = new Bitcoin("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa");
processor.processOrder(items, bitcoin);

鸭子类型(Duck Typing)

JavaScript 作为动态语言, 支持"鸭子类型"——"如果它走起来像鸭子, 叫起来像鸭子, 那它就是鸭子"。对象不需要继承特定的类, 只需要有相应的方法即可:

javascript
// 不同的类, 没有共同的父类
class EmailNotification {
  constructor(email) {
    this.email = email;
  }

  send(message) {
    console.log(`📧 发送邮件到 ${this.email}: ${message}`);
    return { method: "email", recipient: this.email, status: "sent" };
  }
}

class SMSNotification {
  constructor(phoneNumber) {
    this.phoneNumber = phoneNumber;
  }

  send(message) {
    console.log(`📱 发送短信到 ${this.phoneNumber}: ${message}`);
    return { method: "sms", recipient: this.phoneNumber, status: "sent" };
  }
}

class PushNotification {
  constructor(deviceId) {
    this.deviceId = deviceId;
  }

  send(message) {
    console.log(`🔔 推送通知到设备 ${this.deviceId}: ${message}`);
    return { method: "push", recipient: this.deviceId, status: "sent" };
  }
}

class SlackNotification {
  constructor(channel) {
    this.channel = channel;
  }

  send(message) {
    console.log(`💬 发送Slack消息到 #${this.channel}: ${message}`);
    return { method: "slack", recipient: this.channel, status: "sent" };
  }
}

// 通知管理器 - 不关心具体类型, 只要有 send 方法即可
class NotificationManager {
  constructor() {
    this.channels = [];
  }

  addChannel(channel) {
    // 检查是否有 send 方法(鸭子类型检查)
    if (typeof channel.send === "function") {
      this.channels.push(channel);
    } else {
      throw new Error("通知渠道必须有 send 方法");
    }
  }

  // 多态:向所有渠道发送通知
  broadcast(message) {
    console.log(`\n📢 广播消息: "${message}"\n`);

    const results = [];

    for (const channel of this.channels) {
      // 多态调用:每个渠道用自己的方式发送
      const result = channel.send(message);
      results.push(result);
    }

    console.log(`\n✓ 消息已通过 ${results.length} 个渠道发送\n`);

    return results;
  }

  sendToSpecific(message, filter) {
    const filtered = this.channels.filter(filter);

    console.log(`\n📤 发送到特定渠道\n`);

    for (const channel of filtered) {
      channel.send(message);
    }
  }
}

// 使用
const manager = new NotificationManager();

manager.addChannel(new EmailNotification("[email protected]"));
manager.addChannel(new SMSNotification("+1234567890"));
manager.addChannel(new PushNotification("device-abc123"));
manager.addChannel(new SlackNotification("general"));

// 广播到所有渠道
manager.broadcast("系统将在5分钟后维护");

// 发送到特定渠道
manager.sendToSpecific(
  "紧急安全更新",
  (channel) =>
    channel instanceof EmailNotification || channel instanceof SMSNotification
);

策略模式:多态的实际应用

策略模式是多态的经典应用, 它允许在运行时选择算法:

javascript
// 排序策略接口
class SortStrategy {
  sort(array) {
    throw new Error("sort() 必须被实现");
  }

  getName() {
    throw new Error("getName() 必须被实现");
  }
}

// 具体策略:冒泡排序
class BubbleSort extends SortStrategy {
  sort(array) {
    const arr = [...array];
    const n = arr.length;

    for (let i = 0; i < n - 1; i++) {
      for (let j = 0; j < n - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
    }

    return arr;
  }

  getName() {
    return "冒泡排序";
  }
}

// 具体策略:快速排序
class QuickSort extends SortStrategy {
  sort(array) {
    if (array.length <= 1) return array;

    const pivot = array[Math.floor(array.length / 2)];
    const left = array.filter((x) => x < pivot);
    const middle = array.filter((x) => x === pivot);
    const right = array.filter((x) => x > pivot);

    return [...this.sort(left), ...middle, ...this.sort(right)];
  }

  getName() {
    return "快速排序";
  }
}

// 具体策略:归并排序
class MergeSort extends SortStrategy {
  sort(array) {
    if (array.length <= 1) return array;

    const mid = Math.floor(array.length / 2);
    const left = this.sort(array.slice(0, mid));
    const right = this.sort(array.slice(mid));

    return this.merge(left, right);
  }

  merge(left, right) {
    const result = [];
    let i = 0,
      j = 0;

    while (i < left.length && j < right.length) {
      if (left[i] < right[j]) {
        result.push(left[i++]);
      } else {
        result.push(right[j++]);
      }
    }

    return [...result, ...left.slice(i), ...right.slice(j)];
  }

  getName() {
    return "归并排序";
  }
}

// 排序器 - 使用多态
class Sorter {
  constructor(strategy) {
    this.strategy = strategy;
  }

  // 更换策略
  setStrategy(strategy) {
    this.strategy = strategy;
  }

  // 执行排序 - 多态调用
  sort(array) {
    console.log(`使用 ${this.strategy.getName()} 排序...`);

    const startTime = performance.now();
    const result = this.strategy.sort(array);
    const endTime = performance.now();

    console.log(`耗时: ${(endTime - startTime).toFixed(4)}ms\n`);

    return result;
  }
}

// 使用示例
const data = [64, 34, 25, 12, 22, 11, 90, 88, 45, 50, 33, 17];

console.log("原始数组:", data);
console.log();

const sorter = new Sorter(new BubbleSort());
console.log("结果:", sorter.sort(data));

// 动态切换策略
sorter.setStrategy(new QuickSort());
console.log("结果:", sorter.sort(data));

sorter.setStrategy(new MergeSort());
console.log("结果:", sorter.sort(data));

多态的高级应用

事件处理系统

javascript
// 事件基类
class Event {
  constructor(type, data = {}) {
    this.type = type;
    this.data = data;
    this.timestamp = new Date();
  }

  process() {
    throw new Error("process() 必须被实现");
  }
}

// 各种具体事件
class UserLoginEvent extends Event {
  constructor(userId, ipAddress) {
    super("USER_LOGIN", { userId, ipAddress });
  }

  process() {
    console.log(
      `[LOGIN] 用户 ${this.data.userId} 从 ${this.data.ipAddress} 登录`
    );

    // 记录登录日志
    // 更新最后登录时间
    // 检查可疑活动

    return {
      action: "记录登录",
      userId: this.data.userId,
      timestamp: this.timestamp,
    };
  }
}

class OrderPlacedEvent extends Event {
  constructor(orderId, amount, items) {
    super("ORDER_PLACED", { orderId, amount, items });
  }

  process() {
    console.log(
      `[ORDER] 订单 ${this.data.orderId} 已创建, 金额 $${this.data.amount}`
    );

    // 发送确认邮件
    // 更新库存
    // 通知仓库

    return {
      action: "处理订单",
      orderId: this.data.orderId,
      amount: this.data.amount,
    };
  }
}

class PaymentReceivedEvent extends Event {
  constructor(orderId, amount, method) {
    super("PAYMENT_RECEIVED", { orderId, amount, method });
  }

  process() {
    console.log(
      `[PAYMENT] 收到订单 ${this.data.orderId} 的付款 $${this.data.amount}`
    );

    // 更新订单状态
    // 发送收据
    // 触发发货流程

    return {
      action: "处理支付",
      orderId: this.data.orderId,
      method: this.data.method,
    };
  }
}

class ErrorEvent extends Event {
  constructor(errorType, message, stack) {
    super("ERROR", { errorType, message, stack });
  }

  process() {
    console.log(`[ERROR] ${this.data.errorType}: ${this.data.message}`);

    // 记录错误日志
    // 发送警报
    // 如果严重, 通知开发人员

    return {
      action: "记录错误",
      severity: this.#getSeverity(),
      errorType: this.data.errorType,
    };
  }

  #getSeverity() {
    const critical = ["DATABASE_ERROR", "PAYMENT_FAILURE"];
    return critical.includes(this.data.errorType) ? "critical" : "warning";
  }
}

// 事件处理器 - 利用多态统一处理所有事件
class EventProcessor {
  constructor() {
    this.processedEvents = [];
  }

  // 处理单个事件
  handleEvent(event) {
    if (!(event instanceof Event)) {
      throw new Error("必须是 Event 实例");
    }

    console.log(`\n处理事件: ${event.type}`);

    // 多态调用:每个事件类型有自己的处理逻辑
    const result = event.process();

    this.processedEvents.push({
      event,
      result,
      processedAt: new Date(),
    });

    return result;
  }

  // 批量处理事件
  handleBatch(events) {
    console.log(`\n=== 批量处理 ${events.length} 个事件 ===`);

    const results = [];

    for (const event of events) {
      const result = this.handleEvent(event);
      results.push(result);
    }

    console.log(`\n=== 批量处理完成 ===\n`);

    return results;
  }

  getStatistics() {
    const stats = {};

    for (const { event } of this.processedEvents) {
      stats[event.type] = (stats[event.type] || 0) + 1;
    }

    return stats;
  }
}

// 使用
const processor = new EventProcessor();

const events = [
  new UserLoginEvent("user123", "192.168.1.100"),
  new OrderPlacedEvent("ORD-001", 299.99, ["Laptop Mouse"]),
  new PaymentReceivedEvent("ORD-001", 299.99, "Credit Card"),
  new UserLoginEvent("user456", "192.168.1.101"),
  new ErrorEvent("DATABASE_ERROR", "Connection timeout", "..."),
];

processor.handleBatch(events);

console.log("事件统计:");
console.log(processor.getStatistics());
// {
//   USER_LOGIN: 2,
//   ORDER_PLACED: 1,
//   PAYMENT_RECEIVED: 1,
//   ERROR: 1
// }

多态的优势与最佳实践

优势

  1. 代码复用: 通用代码可以处理多种类型的对象
  2. 扩展性: 添加新类型不需要修改现有代码
  3. 可维护性: 每个类型的逻辑独立, 易于理解和修改
  4. 灵活性: 可以在运行时动态选择具体实现

最佳实践

  1. 接口一致性: 确保所有子类实现相同的接口
  2. 避免类型检查: 不要用 instanceoftypeof 来决定行为
  3. 里氏替换原则: 子类对象应该能够替换父类对象而不影响程序正确性
  4. 文档清晰: 明确说明哪些方法需要被重写

小结

多态是面向对象编程的核心力量之一, 它让我们能够:

  • 编写更通用、更灵活的代码
  • 轻松扩展系统功能
  • 保持代码的清晰和可维护性

通过方法重写、接口统一和动态绑定, 多态实现了"一个接口, 多种实现"的优雅设计。无论是简单的图形计算, 还是复杂的业务系统, 多态都能让代码更加优雅和强大。