多态性:一个接口多种实现的艺术
多态是什么
想象你走进一家智能家居展厅, 墙上有一排开关。每个开关看起来都一样, 都有一个"开/关"按钮, 但它们控制的设备完全不同:一个控制灯光, 一个控制空调, 一个控制音响, 还有一个控制窗帘。你不需要学习四种不同的操作方式, 只需要知道"按下开关", 具体的设备会做出相应的响应。
这就是多态(Polymorphism)的精髓:"一个接口, 多种实现"。虽然所有开关都提供相同的接口(按钮), 但每个开关连接到不同的设备, 执行不同的操作。在编程中, 多态让我们能够用统一的方式处理不同类型的对象, 每个对象会根据自己的具体类型做出恰当的响应。
多态的基本原理
多态这个词来自希腊语, 意思是"多种形态"。在面向对象编程中, 多态允许我们:
- 统一接口: 定义通用的方法名称
- 不同实现: 各个子类提供自己独特的实现
- 动态调用: 运行时根据对象的实际类型调用相应的方法
让我们通过一个简单的例子理解多态:
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
// }多态的优势与最佳实践
优势
- 代码复用: 通用代码可以处理多种类型的对象
- 扩展性: 添加新类型不需要修改现有代码
- 可维护性: 每个类型的逻辑独立, 易于理解和修改
- 灵活性: 可以在运行时动态选择具体实现
最佳实践
- 接口一致性: 确保所有子类实现相同的接口
- 避免类型检查: 不要用
instanceof或typeof来决定行为 - 里氏替换原则: 子类对象应该能够替换父类对象而不影响程序正确性
- 文档清晰: 明确说明哪些方法需要被重写
小结
多态是面向对象编程的核心力量之一, 它让我们能够:
- 编写更通用、更灵活的代码
- 轻松扩展系统功能
- 保持代码的清晰和可维护性
通过方法重写、接口统一和动态绑定, 多态实现了"一个接口, 多种实现"的优雅设计。无论是简单的图形计算, 还是复杂的业务系统, 多态都能让代码更加优雅和强大。