名称: oop-polymorphism 用户可调用: false 描述: 在实现面向对象设计中的多态性和接口时使用。用于创建具有可互换组件的灵活、可扩展系统。 允许的工具:
- Bash
- Read
OOP 多态性
掌握多态性,以创建灵活、可扩展的面向对象系统。此技能侧重于通过接口、抽象类和运行时类型替换理解和应用多态行为。
理解多态性
多态性允许通过共同接口统一处理不同类型的对象。它使代码能够与抽象而非具体实现一起工作。
Java 中的基于接口的多态性
// 所有支付方法的共同接口
public interface PaymentMethod {
PaymentResult process(BigDecimal amount);
boolean isValid();
String getDisplayName();
}
// 具体实现
public class CreditCard implements PaymentMethod {
private final String cardNumber;
private final String cardholderName;
private final String expiryDate;
private final String cvv;
public CreditCard(String cardNumber, String cardholderName, String expiryDate, String cvv) {
this.cardNumber = cardNumber;
this.cardholderName = cardholderName;
this.expiryDate = expiryDate;
this.cvv = cvv;
}
@Override
public PaymentResult process(BigDecimal amount) {
if (!isValid()) {
return PaymentResult.failed("无效信用卡");
}
// 信用卡处理逻辑
String transactionId = UUID.randomUUID().toString();
System.out.println("通过信用卡处理 $" + amount + ",卡号末尾 " +
cardNumber.substring(cardNumber.length() - 4));
return PaymentResult.success(transactionId, amount);
}
@Override
public boolean isValid() {
return cardNumber != null &&
cardNumber.length() == 16 &&
!isExpired(expiryDate);
}
@Override
public String getDisplayName() {
return "信用卡末尾 " + cardNumber.substring(cardNumber.length() - 4);
}
private boolean isExpired(String expiryDate) {
// 过期日期验证逻辑
return false;
}
}
public class PayPal implements PaymentMethod {
private final String email;
private final String password;
public PayPal(String email, String password) {
this.email = email;
this.password = password;
}
@Override
public PaymentResult process(BigDecimal amount) {
if (!isValid()) {
return PaymentResult.failed("无效PayPal凭据");
}
String transactionId = UUID.randomUUID().toString();
System.out.println("通过PayPal账户处理 $" + amount + ",邮箱 " + email);
return PaymentResult.success(transactionId, amount);
}
@Override
public boolean isValid() {
return email != null && email.contains("@") && password != null;
}
@Override
public String getDisplayName() {
return "PayPal (" + email + ")";
}
}
public class BankTransfer implements PaymentMethod {
private final String accountNumber;
private final String routingNumber;
private final String accountHolderName;
public BankTransfer(String accountNumber, String routingNumber, String accountHolderName) {
this.accountNumber = accountNumber;
this.routingNumber = routingNumber;
this.accountHolderName = accountHolderName;
}
@Override
public PaymentResult process(BigDecimal amount) {
if (!isValid()) {
return PaymentResult.failed("无效银行账户");
}
String transactionId = UUID.randomUUID().toString();
System.out.println("通过银行转账处理 $" + amount + ",账户持有人 " + accountHolderName);
return PaymentResult.success(transactionId, amount);
}
@Override
public boolean isValid() {
return accountNumber != null &&
routingNumber != null &&
accountNumber.length() > 0;
}
@Override
public String getDisplayName() {
return "银行账户 (" + accountHolderName + ")";
}
}
// 多态使用
public class PaymentProcessor {
private final List<PaymentMethod> paymentMethods;
public PaymentProcessor() {
this.paymentMethods = new ArrayList<>();
}
public void addPaymentMethod(PaymentMethod method) {
paymentMethods.add(method);
}
public PaymentResult processPayment(BigDecimal amount) {
// 尝试每种支付方法直到成功
for (PaymentMethod method : paymentMethods) {
if (method.isValid()) {
System.out.println("尝试支付使用 " + method.getDisplayName());
PaymentResult result = method.process(amount);
if (result.isSuccess()) {
return result;
}
}
}
return PaymentResult.failed("无可用有效支付方法");
}
public List<String> getAvailablePaymentMethods() {
return paymentMethods.stream()
.filter(PaymentMethod::isValid)
.map(PaymentMethod::getDisplayName)
.collect(Collectors.toList());
}
}
// 使用 - 多态性在行动中
PaymentProcessor processor = new PaymentProcessor();
processor.addPaymentMethod(new CreditCard("1234567890123456", "John Doe", "12/25", "123"));
processor.addPaymentMethod(new PayPal("john@example.com", "secret"));
processor.addPaymentMethod(new BankTransfer("9876543210", "123456789", "John Doe"));
PaymentResult result = processor.processPayment(new BigDecimal("99.99"));
Python 中的基于协议的多态性
from typing import Protocol, List, Optional
from abc import ABC, abstractmethod
from dataclasses import dataclass
import json
# 协议定义接口(结构化类型)
class Serializable(Protocol):
"""可序列化对象的协议。"""
def to_dict(self) -> dict:
"""转换为字典。"""
...
def to_json(self) -> str:
"""转换为JSON字符串。"""
...
# 另一个协议
class Identifiable(Protocol):
"""具有ID的对象的协议。"""
@property
def id(self) -> str:
"""获取唯一标识符。"""
...
# 具体实现
@dataclass
class User:
"""用户实现,具有两个协议。"""
_id: str
username: str
email: str
age: int
@property
def id(self) -> str:
return self._id
def to_dict(self) -> dict:
return {
'id': self._id,
'username': self.username,
'email': self.email,
'age': self.age
}
def to_json(self) -> str:
return json.dumps(self.to_dict())
@dataclass
class Product:
"""产品实现,具有两个协议。"""
_id: str
name: str
price: float
category: str
@property
def id(self) -> str:
return self._id
def to_dict(self) -> dict:
return {
'id': self._id,
'name': self.name,
'price': self.price,
'category': self.category
}
def to_json(self) -> str:
return json.dumps(self.to_dict())
@dataclass
class Order:
"""订单实现。"""
_id: str
user_id: str
items: List[str]
total: float
@property
def id(self) -> str:
return self._id
def to_dict(self) -> dict:
return {
'id': self._id,
'user_id': self.user_id,
'items': self.items,
'total': self.total
}
def to_json(self) -> str:
return json.dumps(self.to_dict())
# 使用协议的多态函数
def save_to_file(obj: Serializable, filename: str) -> None:
"""保存任何可序列化对象到文件。"""
with open(filename, 'w') as f:
f.write(obj.to_json())
def get_id(obj: Identifiable) -> str:
"""从任何可标识对象获取ID。"""
return obj.id
def serialize_batch(objects: List[Serializable]) -> str:
"""序列化多个对象。"""
return json.dumps([obj.to_dict() for obj in objects])
# 适用于任何实现协议的对象
user = User("u1", "alice", "alice@example.com", 30)
product = Product("p1", "Laptop", 999.99, "Electronics")
order = Order("o1", "u1", ["p1"], 999.99)
save_to_file(user, "user.json")
save_to_file(product, "product.json")
all_objects = [user, product, order]
batch_json = serialize_batch(all_objects)
Python 中的抽象基类
from abc import ABC, abstractmethod
from typing import List, Dict, Any
from datetime import datetime
# 定义契约的抽象基类
class DataStore(ABC):
"""数据存储的抽象基类。"""
@abstractmethod
def connect(self) -> None:
"""建立到数据存储的连接。"""
pass
@abstractmethod
def disconnect(self) -> None:
"""关闭到数据存储的连接。"""
pass
@abstractmethod
def save(self, key: str, value: Any) -> bool:
"""保存值并指定键。"""
pass
@abstractmethod
def load(self, key: str) -> Optional[Any]:
"""加载指定键的值。"""
pass
@abstractmethod
def delete(self, key: str) -> bool:
"""删除指定键的值。"""
pass
@abstractmethod
def list_keys(self) -> List[str]:
"""列出存储中的所有键。"""
pass
# 具有默认实现的具体方法
def exists(self, key: str) -> bool:
"""检查键是否存在。"""
return self.load(key) is not None
def save_batch(self, items: Dict[str, Any]) -> int:
"""保存多个项目。"""
count = 0
for key, value in items.items():
if self.save(key, value):
count += 1
return count
# 具体实现 - 内存存储
class MemoryStore(DataStore):
"""内存数据存储实现。"""
def __init__(self):
self._data: Dict[str, Any] = {}
self._connected = False
def connect(self) -> None:
self._connected = True
print("内存存储已连接")
def disconnect(self) -> None:
self._connected = False
print("内存存储已断开连接")
def save(self, key: str, value: Any) -> bool:
if not self._connected:
raise RuntimeError("未连接")
self._data[key] = value
return True
def load(self, key: str) -> Optional[Any]:
if not self._connected:
raise RuntimeError("未连接")
return self._data.get(key)
def delete(self, key: str) -> bool:
if not self._connected:
raise RuntimeError("未连接")
if key in self._data:
del self._data[key]
return True
return False
def list_keys(self) -> List[str]:
if not self._connected:
raise RuntimeError("未连接")
return list(self._data.keys())
# 具体实现 - 基于文件的存储
class FileStore(DataStore):
"""基于文件的数据存储实现。"""
def __init__(self, directory: str):
self._directory = directory
self._connected = False
def connect(self) -> None:
import os
os.makedirs(self._directory, exist_ok=True)
self._connected = True
print(f"文件存储已连接: {self._directory}")
def disconnect(self) -> None:
self._connected = False
print("文件存储已断开连接")
def save(self, key: str, value: Any) -> bool:
if not self._connected:
raise RuntimeError("未连接")
import json
filepath = os.path.join(self._directory, f"{key}.json")
try:
with open(filepath, 'w') as f:
json.dump(value, f)
return True
except Exception as e:
print(f"保存 {key} 错误: {e}")
return False
def load(self, key: str) -> Optional[Any]:
if not self._connected:
raise RuntimeError("未连接")
import json
filepath = os.path.join(self._directory, f"{key}.json")
try:
with open(filepath, 'r') as f:
return json.load(f)
except FileNotFoundError:
return None
except Exception as e:
print(f"加载 {key} 错误: {e}")
return None
def delete(self, key: str) -> bool:
if not self._connected:
raise RuntimeError("未连接")
import os
filepath = os.path.join(self._directory, f"{key}.json")
try:
os.remove(filepath)
return True
except FileNotFoundError:
return False
def list_keys(self) -> List[str]:
if not self._connected:
raise RuntimeError("未连接")
import os
return [
f[:-5] for f in os.listdir(self._directory)
if f.endswith('.json')
]
# 多态使用
class DataManager:
"""管理器,适用于任何DataStore。"""
def __init__(self, store: DataStore):
self._store = store
def __enter__(self):
self._store.connect()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._store.disconnect()
def backup(self, source_store: DataStore) -> int:
"""从另一个存储备份。"""
count = 0
for key in source_store.list_keys():
value = source_store.load(key)
if value is not None and self._store.save(key, value):
count += 1
return count
def migrate(self, target_store: DataStore) -> int:
"""将数据迁移到另一个存储。"""
count = 0
for key in self._store.list_keys():
value = self._store.load(key)
if value is not None and target_store.save(key, value):
count += 1
return count
# 使用不同实现
with DataManager(MemoryStore()) as manager:
manager._store.save("user:1", {"name": "Alice", "age": 30})
with DataManager(FileStore("./data")) as manager:
manager._store.save("user:2", {"name": "Bob", "age": 25})
TypeScript 多态性
// 基于接口的多态性
interface Logger {
log(message: string, level: string): void;
flush(): void;
}
interface Formatter {
format(message: string, level: string): string;
}
// 具体实现
class ConsoleLogger implements Logger {
private formatter: Formatter;
constructor(formatter: Formatter) {
this.formatter = formatter;
}
log(message: string, level: string): void {
const formatted = this.formatter.format(message, level);
console.log(formatted);
}
flush(): void {
// 控制台日志立即输出,无需刷新
}
}
class FileLogger implements Logger {
private formatter: Formatter;
private buffer: string[] = [];
private readonly filename: string;
constructor(filename: string, formatter: Formatter) {
this.filename = filename;
this.formatter = formatter;
}
log(message: string, level: string): void {
const formatted = this.formatter.format(message, level);
this.buffer.push(formatted);
if (this.buffer.length >= 10) {
this.flush();
}
}
flush(): void {
if (this.buffer.length === 0) return;
// 写入文件(简化)
const content = this.buffer.join('
');
console.log(`写入到 ${this.filename}:`, content);
this.buffer = [];
}
}
class JsonFormatter implements Formatter {
format(message: string, level: string): string {
return JSON.stringify({
message,
level,
timestamp: new Date().toISOString()
});
}
}
class TextFormatter implements Formatter {
format(message: string, level: string): string {
const timestamp = new Date().toISOString();
return `[${timestamp}] [${level}] ${message}`;
}
}
// 使用多态性的组合日志记录器
class MultiLogger implements Logger {
private loggers: Logger[] = [];
addLogger(logger: Logger): void {
this.loggers.push(logger);
}
log(message: string, level: string): void {
for (const logger of this.loggers) {
logger.log(message, level);
}
}
flush(): void {
for (const logger of this.loggers) {
logger.flush();
}
}
}
// 使用多态性的应用程序
class Application {
private logger: Logger;
constructor(logger: Logger) {
this.logger = logger;
}
run(): void {
this.logger.log("应用程序启动", "INFO");
this.processData();
this.logger.log("应用程序完成", "INFO");
this.logger.flush();
}
private processData(): void {
this.logger.log("处理数据", "DEBUG");
// 处理逻辑
}
}
// 使用 - 可交换实现
const jsonFormatter = new JsonFormatter();
const textFormatter = new TextFormatter();
const consoleLogger = new ConsoleLogger(textFormatter);
const fileLogger = new FileLogger("app.log", jsonFormatter);
const multiLogger = new MultiLogger();
multiLogger.addLogger(consoleLogger);
multiLogger.addLogger(fileLogger);
// 应用程序适用于任何Logger实现
const app = new Application(multiLogger);
app.run();
C# 多态性与接口
// 通知服务的接口
public interface INotificationService
{
Task SendAsync(string recipient, string subject, string body);
bool IsAvailable();
string GetServiceName();
}
// 具体实现
public class EmailNotificationService : INotificationService
{
private readonly string _smtpServer;
private readonly int _port;
private readonly string _username;
private readonly string _password;
public EmailNotificationService(string smtpServer, int port, string username, string password)
{
_smtpServer = smtpServer;
_port = port;
_username = username;
_password = password;
}
public async Task SendAsync(string recipient, string subject, string body)
{
if (!IsAvailable())
throw new InvalidOperationException("电子邮件服务不可用");
Console.WriteLine($"发送电子邮件给 {recipient}");
Console.WriteLine($"主题: {subject}");
Console.WriteLine($"正文: {body}");
// 模拟发送电子邮件
await Task.Delay(100);
}
public bool IsAvailable()
{
return !string.IsNullOrEmpty(_smtpServer);
}
public string GetServiceName()
{
return "电子邮件";
}
}
public class SmsNotificationService : INotificationService
{
private readonly string _apiKey;
private readonly string _phoneNumber;
public SmsNotificationService(string apiKey, string phoneNumber)
{
_apiKey = apiKey;
_phoneNumber = phoneNumber;
}
public async Task SendAsync(string recipient, string subject, string body)
{
if (!IsAvailable())
throw new InvalidOperationException("短信服务不可用");
Console.WriteLine($"发送短信给 {recipient}");
Console.WriteLine($"消息: {subject} - {body}");
await Task.Delay(50);
}
public bool IsAvailable()
{
return !string.IsNullOrEmpty(_apiKey);
}
public string GetServiceName()
{
return "短信";
}
}
public class PushNotificationService : INotificationService
{
private readonly string _appId;
private readonly string _apiKey;
public PushNotificationService(string appId, string apiKey)
{
_appId = appId;
_apiKey = apiKey;
}
public async Task SendAsync(string recipient, string subject, string body)
{
if (!IsAvailable())
throw new InvalidOperationException("推送通知服务不可用");
Console.WriteLine($"发送推送通知给 {recipient}");
Console.WriteLine($"标题: {subject}");
Console.WriteLine($"正文: {body}");
await Task.Delay(30);
}
public bool IsAvailable()
{
return !string.IsNullOrEmpty(_appId) && !string.IsNullOrEmpty(_apiKey);
}
public string GetServiceName()
{
return "推送通知";
}
}
// 多态通知管理器
public class NotificationManager
{
private readonly List<INotificationService> _services;
public NotificationManager()
{
_services = new List<INotificationService>();
}
public void RegisterService(INotificationService service)
{
_services.Add(service);
}
public async Task NotifyAsync(string recipient, string subject, string body)
{
var availableServices = _services.Where(s => s.IsAvailable()).ToList();
if (!availableServices.Any())
{
throw new InvalidOperationException("无可用通知服务");
}
Console.WriteLine($"通过 {availableServices.Count} 个服务发送");
var tasks = availableServices.Select(service =>
NotifyWithServiceAsync(service, recipient, subject, body)
);
await Task.WhenAll(tasks);
}
private async Task NotifyWithServiceAsync(
INotificationService service,
string recipient,
string subject,
string body)
{
try
{
await service.SendAsync(recipient, subject, body);
Console.WriteLine($"{service.GetServiceName()} 通知发送成功");
}
catch (Exception ex)
{
Console.WriteLine($"{service.GetServiceName()} 通知失败: {ex.Message}");
}
}
public List<string> GetAvailableServices()
{
return _services
.Where(s => s.IsAvailable())
.Select(s => s.GetServiceName())
.ToList();
}
}
// 使用
var manager = new NotificationManager();
manager.RegisterService(new EmailNotificationService("smtp.example.com", 587, "user", "pass"));
manager.RegisterService(new SmsNotificationService("api-key", "+1234567890"));
manager.RegisterService(new PushNotificationService("app-id", "api-key"));
await manager.NotifyAsync("user@example.com", "欢迎", "欢迎使用我们的服务!");
方法重载
Java 中的重载
public class Calculator {
// 方法重载 - 相同名称,不同参数
// 添加两个整数
public int add(int a, int b) {
return a + b;
}
// 添加三个整数
public int add(int a, int b, int c) {
return a + b + c;
}
// 添加两个双精度数
public double add(double a, double b) {
return a + b;
}
// 添加整数数组
public int add(int[] numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
// 添加不同类型组合
public double add(int a, double b) {
return a + b;
}
public double add(double a, int b) {
return a + b;
}
}
// 字符串格式化与重载
public class Formatter {
public String format(String text) {
return text.trim();
}
public String format(String text, boolean uppercase) {
String trimmed = format(text);
return uppercase ? trimmed.toUpperCase() : trimmed.toLowerCase();
}
public String format(String text, int maxLength) {
String trimmed = format(text);
return trimmed.length() > maxLength
? trimmed.substring(0, maxLength) + "..."
: trimmed;
}
public String format(String text, boolean uppercase, int maxLength) {
String formatted = format(text, uppercase);
return format(formatted, maxLength);
}
}
C# 中的重载
public class DocumentProcessor
{
// 处理字符串内容
public ProcessResult Process(string content)
{
return new ProcessResult
{
Type = "text",
Length = content.Length,
ProcessedContent = content.Trim()
};
}
// 处理字节数组(二进制内容)
public ProcessResult Process(byte[] content)
{
return new ProcessResult
{
Type = "binary",
Length = content.Length,
ProcessedContent = Convert.ToBase64String(content)
};
}
// 带选项处理
public ProcessResult Process(string content, ProcessOptions options)
{
var result = Process(content);
if (options.RemoveWhitespace)
{
result.ProcessedContent = Regex.Replace(
result.ProcessedContent.ToString(),
@"\s+",
" "
);
}
if (options.MaxLength > 0)
{
var text = result.ProcessedContent.ToString();
result.ProcessedContent = text.Length > options.MaxLength
? text.Substring(0, options.MaxLength)
: text;
}
return result;
}
// 处理文件
public async Task<ProcessResult> ProcessAsync(FileInfo file)
{
var content = await File.ReadAllTextAsync(file.FullName);
return Process(content);
}
// 处理流
public async Task<ProcessResult> ProcessAsync(Stream stream)
{
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
return Process(content);
}
}
运算符重载
C# 运算符重载
public struct Vector3D
{
public double X { get; }
public double Y { get; }
public double Z { get; }
public Vector3D(double x, double y, double z)
{
X = x;
Y = y;
Z = z;
}
// 二元运算符重载
public static Vector3D operator +(Vector3D a, Vector3D b)
{
return new Vector3D(a.X + b.X, a.Y + b.Y, a.Z + b.Z);
}
public static Vector3D operator -(Vector3D a, Vector3D b)
{
return new Vector3D(a.X - b.X, a.Y - b.Y, a.Z - b.Z);
}
public static Vector3D operator *(Vector3D v, double scalar)
{
return new Vector3D(v.X * scalar, v.Y * scalar, v.Z * scalar);
}
public static Vector3D operator *(double scalar, Vector3D v)
{
return v * scalar;
}
public static Vector3D operator /(Vector3D v, double scalar)
{
if (scalar == 0)
throw new DivideByZeroException();
return new Vector3D(v.X / scalar, v.Y / scalar, v.Z / scalar);
}
// 一元运算符重载
public static Vector3D operator -(Vector3D v)
{
return new Vector3D(-v.X, -v.Y, -v.Z);
}
// 比较运算符
public static bool operator ==(Vector3D a, Vector3D b)
{
return a.X == b.X && a.Y == b.Y && a.Z == b.Z;
}
public static bool operator !=(Vector3D a, Vector3D b)
{
return !(a == b);
}
// 重写Object方法
public override bool Equals(object obj)
{
return obj is Vector3D vector && this == vector;
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y, Z);
}
public override string ToString()
{
return $"({X}, {Y}, {Z})";
}
// 额外向量操作
public double Magnitude()
{
return Math.Sqrt(X * X + Y * Y + Z * Z);
}
public Vector3D Normalize()
{
double mag = Magnitude();
return mag > 0 ? this / mag : this;
}
public double Dot(Vector3D other)
{
return X * other.X + Y * other.Y + Z * other.Z;
}
public Vector3D Cross(Vector3D other)
{
return new Vector3D(
Y * other.Z - Z * other.Y,
Z * other.X - X * other.Z,
X * other.Y - Y * other.X
);
}
}
// 使用
var v1 = new Vector3D(1, 2, 3);
var v2 = new Vector3D(4, 5, 6);
var v3 = v1 + v2; // (5, 7, 9)
var v4 = v1 * 2; // (2, 4, 6)
var v5 = -v1; // (-1, -2, -3)
Python 魔术方法(运算符重载)
class Money:
"""带运算符重载的货币类。"""
def __init__(self, amount: float, currency: str = "USD"):
self.amount = amount
self.currency = currency
def __add__(self, other):
"""添加两个货币金额。"""
if isinstance(other, Money):
if self.currency != other.currency:
raise ValueError("无法添加不同货币")
return Money(self.amount + other.amount, self.currency)
elif isinstance(other, (int, float)):
return Money(self.amount + other, self.currency)
return NotImplemented
def __sub__(self, other):
"""减去两个货币金额。"""
if isinstance(other, Money):
if self.currency != other.currency:
raise ValueError("无法减去不同货币")
return Money(self.amount - other.amount, self.currency)
elif isinstance(other, (int, float)):
return Money(self.amount - other, self.currency)
return NotImplemented
def __mul__(self, other):
"""货币乘以数字。"""
if isinstance(other, (int, float)):
return Money(self.amount * other, self.currency)
return NotImplemented
def __truediv__(self, other):
"""货币除以数字。"""
if isinstance(other, (int, float)):
if other == 0:
raise ValueError("不能除以零")
return Money(self.amount / other, self.currency)
return NotImplemented
def __eq__(self, other):
"""检查相等性。"""
if not isinstance(other, Money):
return False
return self.amount == other.amount and self.currency == other.currency
def __lt__(self, other):
"""小于比较。"""
if not isinstance(other, Money):
return NotImplemented
if self.currency != other.currency:
raise ValueError("无法比较不同货币")
return self.amount < other.amount
def __le__(self, other):
"""小于或等于比较。"""
return self == other or self < other
def __gt__(self, other):
"""大于比较。"""
if not isinstance(other, Money):
return NotImplemented
if self.currency != other.currency:
raise ValueError("无法比较不同货币")
return self.amount > other.amount
def __ge__(self, other):
"""大于或等于比较。"""
return self == other or self > other
def __str__(self):
"""字符串表示。"""
return f"{self.currency} {self.amount:.2f}"
def __repr__(self):
"""开发者表示。"""
return f"Money({self.amount}, {self.currency!r})"
def __hash__(self):
"""用于集合/字典的哈希。"""
return hash((self.amount, self.currency))
# 使用
price = Money(19.99)
tax = Money(2.00)
total = price + tax # Money(21.99, 'USD')
discounted = total * 0.9 # Money(19.791, 'USD')
print(total > price) # True
鸭子类型和结构多态性
Python 中的鸭子类型
# 无显式接口 - 对象只需正确方法
class FileWriter:
"""写入文件。"""
def __init__(self, filename: str):
self.file = open(filename, 'w')
def write(self, data: str) -> None:
self.file.write(data)
def close(self) -> None:
self.file.close()
class StringWriter:
"""写入字符串缓冲区。"""
def __init__(self):
self.buffer = []
def write(self, data: str) -> None:
self.buffer.append(data)
def close(self) -> None:
pass # 无需关闭
def get_value(self) -> str:
return ''.join(self.buffer)
class NetworkWriter:
"""写入网络套接字。"""
def __init__(self, host: str, port: int):
self.host = host
self.port = port
self.connected = True
def write(self, data: str) -> None:
print(f"发送到 {self.host}:{self.port}: {data}")
def close(self) -> None:
self.connected = False
print("连接关闭")
# 适用于任何“写入器类”对象的函数
def save_report(writer, title: str, data: List[str]) -> None:
"""使用任何写入器保存报告(鸭子类型)。"""
writer.write(f"报告: {title}
")
writer.write("=" * 50 + "
")
for item in data:
writer.write(f"- {item}
")
writer.close()
# 所有对象都适用于相同函数
save_report(FileWriter("report.txt"), "销售", ["项目 1", "项目 2"])
save_report(StringWriter(), "库存", ["产品 A", "产品 B"])
save_report(NetworkWriter("server.com", 8080), "指标", ["CPU: 50%"])
何时使用此技能
应用多态性时:
- 构建可扩展插件架构
- 创建可互换实现
- 编写针对接口/抽象的代码
- 实现策略模式
- 构建依赖注入系统
- 创建框架钩子和扩展点
- 支持多种数据格式或协议
- 实现命令模式
- 构建通知或消息系统
- 创建抽象数据访问层
- 支持多种渲染引擎
- 实现访问者模式
- 构建具有多态状态的状态机
- 创建返回多态类型的工厂方法
- 设计可测试代码和模拟对象
最佳实践
- 面向接口编程,而非实现
- 使用抽象基类共享行为
- 保持接口小而专注(ISP)
- 使用多态性消除switch语句
- 偏好组合而非继承以提高灵活性
- 适当使多态方法为virtual/abstract
- 使用依赖注入处理多态依赖
- 文档化实现所需的契约/行为
- 在合理处提供默认实现
- 结合多态性使用泛型以确保类型安全
- 重载运算符时重写相等方法
- 保持多态层次浅
- 使用工厂模式创建多态对象
- 测试多态接口的每个实现
- 考虑使用协议/结构化类型以提高灵活性
常见陷阱
- 违反里氏替换原则
- 创建过多小接口
- 未跨实现提供一致行为
- 过度使用继承实现多态性
- 忘记重写Object方法(equals, hashCode)
- 创建泄漏抽象
- 在接口中混合抽象层次
- 未在多态代码中处理null/None
- 在多态类型之间创建循环依赖
- 重载具有相似但不同语义的方法
- 未考虑虚方法调用性能
- 使用反射而非多态性
- 创建具有过多方法的神接口
- 未测试实现的可替代性
- 耦合到具体类型而非抽象
资源
- 设计模式:可复用面向对象软件的元素
- 里氏替换原则: https://en.wikipedia.org/wiki/Liskov_substitution_principle
- Python 协议: https://peps.python.org/pep-0544/
- C# 接口: https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces
- Java 多态性教程: https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html
- TypeScript 接口: https://www.typescriptlang.org/docs/handbook/interfaces.html
- Effective Java by Joshua Bloch(第64项:通过接口引用对象)