文章目录
简介
设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发中针对常见问题 的可复用解决方案 。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。它并非现成代码,而是一种最佳实践的模板或思想,帮助开发者构建更健壮、可维护、可扩展的系统。
优点
- 提供了一种共享的设计词汇和概念,使开发人员能够更好地沟通和理解彼此的设计意图。
- 提供了经过验证的解决方案,可以提高软件的可维护性、可复用性和灵活性。
- 促进了代码的重用,避免了重复的设计和实现。
- 通过遵循设计模式,可以减少系统中的错误和问题,提高代码质量。
核心设计原则
| 原则 | 解释 | 举例 |
|---|---|---|
| 单一职责 | 一个类只为一个原因修改 | UI逻辑与业务逻辑分离 |
| 开闭原则(Open Close Principle) | 对扩展开放,对修改关闭 | 用策略模式增加算法,不改原有类 |
| 里氏替换(Liskov Substitution Principle) | 子类能完全替换父类 | 子类不重写父类非抽象方法破坏行为 |
| 接口隔离(Interface Segregation Principle) | 接口应小而专一 | 不要设计臃肿的Worker接口,拆分Eatable、Workable |
| 依赖倒置(Dependence Inversion Principle) | 依赖抽象而非具体 | 类依赖Logger接口而非FileLogger具体类 |
| 迪米特法则,又称最少知道原则(Demeter Principle) | 只与直接朋友通信 | A不直接调用B的内部对象方法 |
| 组合优于继承(Composite Reuse Principle) | 用组合实现行为复用 | Car包含Engine,而非继承Vehicle |
模式类型
创建型模式
关注对象创建:这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
| 模式 | 核心作用 |
|---|---|
| 工厂方法 | 延迟实例化到子类 |
| 单例 | 确保全局唯一实例 |
| 抽象工厂 | 创建一系列相关对象 |
| 建造者 | 分步构建复杂对象 |
| 原型 | 克隆对象而非new |
工厂方法
工厂方法模式(Factory Method Pattern)定义了一个用于创建对象的接口,但由子类决定实例化哪一个类。工厂方法将对象的实例化延迟到子类中完成。
-
核心角色(4个关键角色)
角色 英文名 职责 本示例对应 产品接口 Product 定义产品的公共接口 Logistics 具体产品 Concrete Product 实现产品接口的具体类 ExpressLogistics、FreightLogistics 工厂接口 Factory 声明工厂方法(返回产品类型) LogisticsFactory 具体工厂 Concrete Factory 实现工厂方法,创建具体产品 ExpressFactory、FreightFactory 角色关系图
text客户端 → 工厂接口 ← 具体工厂 ↓ 产品接口 ← 具体产品 -
优缺点分析
✅ 优点
优点 说明 单一职责原则 将产品创建代码集中在一个工厂类中,避免创建逻辑散落各处 开闭原则 新增产品类型时,只需添加对应工厂类,无需修改现有代码 解耦 客户端只依赖工厂接口和产品接口,不依赖具体产品类 扩展性强 可以轻松引入新产品类型(如冷链物流) 代码复用 工厂方法可以被多个客户端共享 ❌ 缺点
缺点 说明 解决方案 类数量爆炸 每增加一个产品,就需要增加一个具体工厂类 结合简单工厂或反射机制 增加复杂度 引入大量抽象类和接口,代码结构变复杂 仅在必要时使用 理解成本 初学者难以理解"延迟实例化"的思想 提供充分示例和文档 调试困难 创建逻辑被分散到多个工厂类中 统一日志输出 -
应用场景
场景类型 具体描述 真实案例 编译时无法预知类型 客户端不知道需要创建哪种具体产品 日志框架(文件日志/数据库日志) 类层次结构复杂 产品类有复杂的继承树 UI控件库(按钮/输入框的主题化) 创建逻辑复杂 对象的创建需要复杂初始化、配置 数据库连接池(不同数据库连接) 扩展性要求高 未来可能频繁增加新产品类型 支付系统(新增支付宝/微信支付) 框架开发 需要将创建逻辑留给框架使用者实现 ORM框架的SessionFactory -
应用示例:物流系统的订单发货处理
typescript// ==================== 角色1:产品接口 ==================== /** * 产品接口:定义所有物流单必须实现的方法 * 作用:将客户端与具体产品解耦 */ interface Logistics { /** 执行发货 */ deliver(): void; /** 获取运单号 */ getTrackingNumber(): string; /** 获取预计到达时间(天) */ getEstimatedDays(): number; } // ==================== 角色2:具体产品 ==================== /** * 具体产品A:快递物流单 * 特点:快速、轻量、适合小件商品 */ class ExpressLogistics implements Logistics { private readonly trackingNumber: string; private readonly estimatedDays: number = 2; constructor() { // 生成快递特有的运单号格式 this.trackingNumber = `EXP-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`; this.initialize(); } /** 私有初始化方法:演示复杂创建逻辑 */ private initialize(): void { console.log(" → 初始化快递物流:检查包裹重量、分配快递员"); } deliver(): void { console.log(` ✅ 【快递】上门派送,预计${this.estimatedDays}天内送达`); } getTrackingNumber(): string { return this.trackingNumber; } getEstimatedDays(): number { return this.estimatedDays; } } /** * 具体产品B:货运物流单 * 特点:重型、缓慢、适合大宗货物 */ class FreightLogistics implements Logistics { private readonly trackingNumber: string; private readonly estimatedDays: number = 7; constructor() { this.trackingNumber = `FRT-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`; this.loadCargo(); } /** 私有初始化方法:货运特有的准备流程 */ private loadCargo(): void { console.log(" → 初始化货运物流:安排货车、计算载重"); } deliver(): void { console.log(` ✅ 【货运】专车运输,预计${this.estimatedDays}天内送达`); } getTrackingNumber(): string { return this.trackingNumber; } getEstimatedDays(): number { return this.estimatedDays; } } /** * 具体产品C:冷链物流(扩展示例,展示开闭原则) */ class ColdChainLogistics implements Logistics { private readonly trackingNumber: string; private readonly estimatedDays: number = 3; constructor() { this.trackingNumber = `COLD-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`; console.log(" → 初始化冷链物流:启动冷藏设备、检查温度传感器"); } deliver(): void { console.log(` ✅ 【冷链】恒温运输,预计${this.estimatedDays}天内送达(全程温控)`); } getTrackingNumber(): string { return this.trackingNumber; } getEstimatedDays(): number { return this.estimatedDays; } } // ==================== 角色3:工厂接口 ==================== /** * 工厂接口:声明工厂方法 * 作用:定义创建产品的接口,但不实现具体创建逻辑 */ interface LogisticsFactory { /** 工厂方法:创建物流单对象 */ createLogistics(): Logistics; /** 可选:创建带预设配置的物流单(工厂方法的变体) */ createLogisticsWithConfig?(config: any): Logistics; } // ==================== 角色4:具体工厂 ==================== /** * 具体工厂A:生产快递物流单 * 职责:封装快递物流的创建细节 */ class ExpressFactory implements LogisticsFactory { createLogistics(): Logistics { console.log("🏭 快递工厂:开始创建快递物流单"); return new ExpressLogistics(); } } /** * 具体工厂B:生产货运物流单 */ class FreightFactory implements LogisticsFactory { createLogistics(): Logistics { console.log("🏭 货运工厂:开始创建货运物流单"); return new FreightLogistics(); } } /** * 具体工厂C:生产冷链物流单 */ class ColdChainFactory implements LogisticsFactory { createLogistics(): Logistics { console.log("🏭 冷链工厂:开始创建冷链物流单"); return new ColdChainLogistics(); } } // ==================== 客户端代码 ==================== /** * 客户端:订单处理服务 * 特点:只依赖工厂接口,不依赖具体产品和具体工厂 */ class OrderService { private readonly factory: LogisticsFactory; constructor(factory: LogisticsFactory) { this.factory = factory; // 依赖注入:工厂由外部传入 } /** * 处理订单核心方法 * 完全不知道具体创建的是快递还是货运,只知道调用工厂方法 */ processOrder(orderId: string): void { console.log(`\n📋 处理订单: ${orderId}`); // 通过工厂方法创建物流单(核心调用) const logistics = this.factory.createLogistics(); console.log(` 📦 运单号: ${logistics.getTrackingNumber()}`); console.log(` ⏰ 预计: ${logistics.getEstimatedDays()}天`); logistics.deliver(); } } /** * 高级客户端:工厂注册表(演示动态扩展能力) */ class LogisticsService { private static factories: Map<string, LogisticsFactory> = new Map(); /** 注册新产品类型(体现开闭原则) */ static registerLogisticsType(type: string, factory: LogisticsFactory): void { this.factories.set(type, factory); } /** 动态创建物流单 */ static createLogistics(type: string): Logistics | null { const factory = this.factories.get(type); if (!factory) { console.log(`❌ 不支持的物流类型: ${type}`); return null; } return factory.createLogistics(); } /** 动态处理订单 */ static processOrder(orderId: string, type: string): void { const logistics = this.createLogistics(type); if (logistics) { console.log(`\n📋 订单: ${orderId}`); console.log(` 📦 运单号: ${logistics.getTrackingNumber()}`); logistics.deliver(); } } } // 注册所有支持的物流类型(扩展新产品只需在此注册) LogisticsService.registerLogisticsType("express", new ExpressFactory()); LogisticsService.registerLogisticsType("freight", new FreightFactory()); LogisticsService.registerLogisticsType("coldchain", new ColdChainFactory()); // ==================== 程序入口:演示 ==================== function main() { console.log("=" .repeat(60)); console.log("工厂方法模式演示 - 物流系统"); console.log("=" .repeat(60)); // 演示1:传统方式(显式创建工厂) console.log("\n【演示1:显式使用具体工厂】"); const expressService = new OrderService(new ExpressFactory()); expressService.processOrder("ORD-001"); const freightService = new OrderService(new FreightFactory()); freightService.processOrder("ORD-002"); // 演示2:动态工厂注册表(更灵活) console.log("\n【演示2:动态工厂注册表】"); LogisticsService.processOrder("ORD-003", "express"); LogisticsService.processOrder("ORD-004", "freight"); LogisticsService.processOrder("ORD-005", "coldchain"); // 演示3:扩展新产品(体现开闭原则) console.log("\n【演示3:产品扩展能力】"); console.log("🌟 新增'国际快递'物流类型只需:"); console.log(" 1. 创建 InternationalLogistics 类实现 Logistics 接口"); console.log(" 2. 创建 InternationalFactory 实现 LogisticsFactory 接口"); console.log(" 3. 调用 registerLogisticsType('international', new InternationalFactory())"); // 演示4:类型不存在的错误处理 console.log("\n【演示4:错误处理】"); LogisticsService.processOrder("ORD-999", "invalid_type"); } // 运行 main();
抽象工厂
抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
-
通俗理解 :
1.工厂方法 = 生产单一产品(如只生产椅子)
2.抽象工厂 = 生产整套家具(椅子 + 沙发 + 茶几 + 桌子)
-
核心角色
角色 英文名 职责 本示例对应 抽象产品A Abstract Product A 定义产品A的接口 IChair(椅子) 抽象产品B Abstract Product B 定义产品B的接口 ISofa(沙发) 具体产品 Concrete Product 实现抽象产品的具体类 ModernChair、VictorianSofa 等 抽象工厂 Abstract Factory 声明创建产品族的接口 IFurnitureFactory 具体工厂 Concrete Factory 实现抽象工厂,创建产品族 ModernFactory、VictorianFactory -
优缺点分析
✅ 优点
优点 说明 一致性保证 保证同一产品族的对象能一起工作,风格统一 开闭原则(纵向) 新增产品族(新风格)时,只需添加新的具体工厂,无需修改现有代码 开闭原则(横向) 新增产品类型时,需要修改抽象工厂(破坏开闭,见缺点) 解耦客户端 客户端只依赖抽象接口,不依赖具体产品类 便于产品族切换 只需更换具体工厂,就能切换整个产品系列 ❌ 缺点
缺点 说明 解决方案 扩展新产品困难 需要新增产品类型(如茶几)时,必须修改抽象工厂接口及其所有实现 使用反射或依赖注入框架 类数量爆炸 每增加一个产品族,就增加一个具体工厂;每增加一个产品类型,就增加一组具体产品 结合简单工厂简化 复杂度高 引入大量接口和抽象类,增加理解成本 仅在必要时使用,提供充分文档 过度设计风险 小项目可能不需要如此严格的抽象 先用工厂方法,必要时重构为抽象工厂 -
应用场景
场景类型 具体描述 真实案例 UI 主题切换 同一套UI组件(按钮、输入框、对话框)的多种主题风格 暗色模式/亮色模式、企业主题/个人主题 跨平台适配 同一业务逻辑在不同操作系统下的不同实现 Windows/Linux/Mac 的窗口控件 品牌产品线 同一产品线的系列产品 小米生态链(手机+手环+电视)、苹果全家桶 数据库访问层 对不同数据库(MySQL/Oracle/PostgreSQL)的统一操作 连接、命令、事务等对象的创建 游戏角色创建 不同种族/阵营的角色装备系列 人族武器+防具、兽族武器+防具 文档生成系统 不同格式(PDF/Word/HTML)的文档元素 段落、表格、图片的创建 -
应用示例:家具商城的多风格产品线
支持两种风格:现代风格 (Modern)、维多利亚风格 (Victorian)
每种风格包含:椅子 (Chair)、沙发 (Sofa)、桌子 (Table)
客户端可以一键切换风格,而无需修改业务逻辑
typescript// ==================== 角色1:抽象产品A - 椅子 ==================== /** * 椅子产品接口 * 定义所有椅子必须实现的方法 */ interface IChair { /** 坐下 */ sitOn(): void; /** 是否有扶手 */ hasArmrest(): boolean; /** 材质 */ getMaterial(): string; /** 价格 */ getPrice(): number; } // ==================== 角色2:抽象产品B - 沙发 ==================== /** * 沙发产品接口 */ interface ISofa { /** 躺下 */ lieOn(): void; /** 座位数量 */ getSeatCount(): number; /** 是否可折叠 */ isFoldable(): boolean; /** 材质 */ getMaterial(): string; } // ==================== 角色3:抽象产品C - 桌子 ==================== /** * 桌子产品接口 */ interface ITable { /** 放置物品 */ placeItem(item: string): void; /** 桌面形状 */ getShape(): string; /** 承重能力(kg) */ getLoadCapacity(): number; } // ==================== 具体产品系列A:现代风格 ==================== /** * 现代风格椅子 */ class ModernChair implements IChair { private readonly material: string = "碳纤维"; private readonly price: number = 1299; sitOn(): void { console.log(" 🪑 坐在现代风格椅子上,符合人体工学,非常舒适"); } hasArmrest(): boolean { return true; } getMaterial(): string { return this.material; } getPrice(): number { return this.price; } } /** * 现代风格沙发 */ class ModernSofa implements ISofa { private readonly material: string = "科技布"; private readonly seatCount: number = 3; lieOn(): void { console.log(" 🛋️ 躺在现代风格沙发上,柔软有支撑,适合午睡"); } getSeatCount(): number { return this.seatCount; } isFoldable(): boolean { return true; // 现代沙发可折叠 } getMaterial(): string { return this.material; } } /** * 现代风格桌子 */ class ModernTable implements ITable { private readonly shape: string = "圆形"; private readonly loadCapacity: number = 100; placeItem(item: string): void { console.log(` 🍽️ 在${this.shape}现代桌子上放置 ${item}`); } getShape(): string { return this.shape; } getLoadCapacity(): number { return this.loadCapacity; } } // ==================== 具体产品系列B:维多利亚风格 ==================== /** * 维多利亚风格椅子 */ class VictorianChair implements IChair { private readonly material: string = "实木+天鹅绒"; private readonly price: number = 2599; sitOn(): void { console.log(" 🪑 坐在维多利亚风格椅子上,复古优雅,尽显贵族气质"); } hasArmrest(): boolean { return true; } getMaterial(): string { return this.material; } getPrice(): number { return this.price; } } /** * 维多利亚风格沙发 */ class VictorianSofa implements ISofa { private readonly material: string = "真皮+铆钉"; private readonly seatCount: number = 2; lieOn(): void { console.log(" 🛋️ 躺在维多利亚风格沙发上,古典奢华,体验贵族生活"); } getSeatCount(): number { return this.seatCount; } isFoldable(): boolean { return false; // 古典沙发不可折叠 } getMaterial(): string { return this.material; } } /** * 维多利亚风格桌子 */ class VictorianTable implements ITable { private readonly shape: string = "长方形"; private readonly loadCapacity: number = 150; placeItem(item: string): void { console.log(` 🍽️ 在${this.shape}维多利亚桌子上放置 ${item},充满古典韵味`); } getShape(): string { return this.shape; } getLoadCapacity(): number { return this.loadCapacity; } } // ==================== 角色4:抽象工厂 ==================== /** * 家具工厂接口 * 声明创建产品族的所有方法 */ interface IFurnitureFactory { /** 创建椅子 */ createChair(): IChair; /** 创建沙发 */ createSofa(): ISofa; /** 创建桌子 */ createTable(): ITable; /** 可选:获取工厂描述 */ getFactoryDescription(): string; } // ==================== 角色5:具体工厂 ==================== /** * 具体工厂A:现代家具工厂 * 生产整套现代风格家具 */ class ModernFurnitureFactory implements IFurnitureFactory { createChair(): IChair { console.log(" 🏭 现代工厂:生产现代风格椅子"); return new ModernChair(); } createSofa(): ISofa { console.log(" 🏭 现代工厂:生产现代风格沙发"); return new ModernSofa(); } createTable(): ITable { console.log(" 🏭 现代工厂:生产现代风格桌子"); return new ModernTable(); } getFactoryDescription(): string { return "🏢 现代家具工厂 - 简约、时尚、实用"; } } /** * 具体工厂B:维多利亚家具工厂 * 生产整套维多利亚风格家具 */ class VictorianFurnitureFactory implements IFurnitureFactory { createChair(): IChair { console.log(" 🏭 维多利亚工厂:生产维多利亚风格椅子"); return new VictorianChair(); } createSofa(): ISofa { console.log(" 🏭 维多利亚工厂:生产维多利亚风格沙发"); return new VictorianSofa(); } createTable(): ITable { console.log(" 🏭 维多利亚工厂:生产维多利亚风格桌子"); return new VictorianTable(); } getFactoryDescription(): string { return "🏛️ 维多利亚家具工厂 - 古典、奢华、艺术"; } } // ==================== 客户端代码 ==================== /** * 客户端:家具商城 * 只依赖抽象工厂接口,不知道具体产品类 */ class FurnitureStore { private factory: IFurnitureFactory; private orderHistory: string[] = []; constructor(factory: IFurnitureFactory) { this.factory = factory; console.log(`✨ 家具商城已切换到: ${factory.getFactoryDescription()}`); } /** * 切换工厂(运行时动态更换产品系列) */ switchFactory(factory: IFurnitureFactory): void { this.factory = factory; console.log(`\n🔄 切换风格: ${factory.getFactoryDescription()}`); } /** * 购买整套家具套餐 */ buyFurnitureSet(): void { console.log("\n📦 购买整套家具套餐:"); // 创建整套家具(产品族) const chair = this.factory.createChair(); const sofa = this.factory.createSofa(); const table = this.factory.createTable(); // 使用家具 console.log("\n 使用体验:"); chair.sitOn(); sofa.lieOn(); table.placeItem("咖啡"); // 统计信息 const totalPrice = chair.getPrice() + (sofa as any).getPrice?.() || 0 + (table as any).getPrice?.() || 0; console.log(`\n 💰 套餐总价: ¥${totalPrice}`); // 记录订单 this.orderHistory.push(`整套家具 - ${this.factory.getFactoryDescription()}`); } /** * 单独购买椅子(可以选择性购买单个产品) */ buyChair(): void { console.log("\n🪑 单独购买椅子:"); const chair = this.factory.createChair(); console.log(` 材质: ${chair.getMaterial()}`); console.log(` 价格: ¥${chair.getPrice()}`); chair.sitOn(); this.orderHistory.push(`椅子 - ${this.factory.getFactoryDescription()}`); } /** * 显示订单历史 */ showOrderHistory(): void { console.log("\n📋 订单历史:"); this.orderHistory.forEach((order, index) => { console.log(` ${index + 1}. ${order}`); }); } } /** * 高级客户端:工厂注册表 + 配置驱动 */ class FurnitureFactoryRegistry { private static factories: Map<string, IFurnitureFactory> = new Map(); static register(style: string, factory: IFurnitureFactory): void { this.factories.set(style, factory); } static getFactory(style: string): IFurnitureFactory | null { const factory = this.factories.get(style); if (!factory) { console.log(`❌ 不支持的家具风格: ${style}`); return null; } return factory; } static getAllStyles(): string[] { return Array.from(this.factories.keys()); } } // 注册所有支持的家具风格 FurnitureFactoryRegistry.register("modern", new ModernFurnitureFactory()); FurnitureFactoryRegistry.register("victorian", new VictorianFurnitureFactory()); // ==================== 程序入口 ==================== function main() { console.log("=" .repeat(70)); console.log("抽象工厂模式演示 - 家具商城系统"); console.log("=" .repeat(70)); // 演示1:传统方式(显式创建工厂) console.log("\n【演示1:使用现代风格】"); const modernStore = new FurnitureStore(new ModernFurnitureFactory()); modernStore.buyFurnitureSet(); console.log("\n【演示2:切换为维多利亚风格】"); const victorianStore = new FurnitureStore(new VictorianFurnitureFactory()); victorianStore.buyFurnitureSet(); // 演示2:运行时动态切换风格 console.log("\n【演示3:动态切换风格】"); const store = new FurnitureStore(new ModernFurnitureFactory()); store.buyFurnitureSet(); // 动态切换到维多利亚风格 store.switchFactory(new VictorianFurnitureFactory()); store.buyChair(); // 演示3:注册表方式(生产环境推荐) console.log("\n【演示4:注册表动态创建】"); const styles = FurnitureFactoryRegistry.getAllStyles(); console.log(`支持的风格: ${styles.join(", ")}`); const selectedStyle = "modern"; // 通常从配置文件或用户选择获取 const factory = FurnitureFactoryRegistry.getFactory(selectedStyle); if (factory) { const configStore = new FurnitureStore(factory); configStore.buyFurnitureSet(); configStore.showOrderHistory(); } // 演示4:扩展示例 - 展示开闭原则 console.log("\n【演示5:产品族扩展能力】"); console.log("🌟 新增'北欧风格 (Nordic)'只需:"); console.log(" 1. 创建 NordicChair, NordicSofa, NordicTable 实现产品接口"); console.log(" 2. 创建 NordicFactory 实现 IFurnitureFactory"); console.log(" 3. 调用 FurnitureFactoryRegistry.register('nordic', new NordicFactory())"); console.log(" 4. 客户端无需任何修改即可使用"); // 演示5:产品单独使用(不通过工厂) console.log("\n【演示6:产品独立性】"); console.log("抽象工厂的每个产品也可独立使用:"); const standaloneChair = new ModernChair(); standaloneChair.sitOn(); console.log(` 独立购买价格: ¥${standaloneChair.getPrice()}`); } // 运行 main();
原型
原型模式(Prototype Pattern)通过复制一个已有的对象(原型)来创建新对象,而不是通过 new 关键字实例化。被复制的对象被称为原型,新对象是原型的克隆。
-
通俗理解
1.传统方式:像按照图纸建房子(每次都要重新打地基、砌墙...)
2.原型模式:像使用复印机复印文件(已有的文件直接复印,快速复制内容) -
核心角色
角色 英文名 职责 本示例对应 抽象原型 Prototype 声明克隆方法的接口 ICloneable 具体原型 Concrete Prototype 实现克隆方法,复制自身 GameCharacter、Document 客户端 Client 通过克隆原型创建新对象 GameManager、DocumentEditor
原型模式的工作流程
bash
┌─────────────┐ clone() ┌─────────────┐
│ 原型对象 │ ───────────────→ │ 克隆对象 │
│ (原始数据) │ │ (副本数据) │
└─────────────┘ └─────────────┘
↓ ↓
保持不变 可独立修改
-
优缺点分析
✅ 优点
优点 说明 性能优化 避免昂贵的初始化操作(如数据库查询、IO操作、网络请求) 简化对象创建 复制复杂对象比手动 new 并逐个设置属性更简单 动态配置 运行时可以动态加载原型,无需硬编码类名 避免构造器污染 当构造器参数很多时,克隆可以避免传递大量参数 保留对象状态 可以复制对象在某个时刻的精确状态(快照) 减少子类爆炸 无需为每种配置创建子类,通过克隆+修改即可 ❌ 缺点
缺点 说明 解决方案 深拷贝 vs 浅拷贝陷阱 默认克隆可能只复制引用,导致对象共享 明确实现深拷贝 循环引用问题 对象间相互引用可能导致无限递归 使用序列化方式克隆 克隆方法实现复杂 对象包含复杂引用类型时,需要精心设计克隆逻辑 使用序列化辅助克隆 需要实现克隆接口 每个类都需要实现克隆方法 使用代码生成工具 破坏单例 单例模式不能直接克隆 单例类不实现克隆或返回自身 -
应用场景
场景类型 具体描述 真实案例 对象创建成本高 创建对象需要大量数据库查询、IO操作或网络请求 从数据库加载配置对象后克隆使用 需要保留对象状态 需要保存对象的某个时刻的快照 游戏存档、IDE的撤销/重做功能 避免子类爆炸 需要大量相似但略有差异的对象 游戏中不同属性的怪物、文档模板 运行时动态创建 编译时无法确定具体类名 插件系统、动态加载模块 循环引用结构 对象包含循环引用的复杂数据结构 组织结构树、XML DOM节点克隆 性能敏感场景 频繁创建相同配置的对象 连接池中的连接对象克隆 -
应用示例:游戏角色克隆系统
typescript// ==================== 角色1:抽象原型 ==================== /** * 可克隆接口 * 定义所有可克隆对象必须实现的方法 */ interface ICloneable<T> { /** 浅拷贝克隆 */ clone(): T; /** 深拷贝克隆 */ deepClone(): T; } // ==================== 角色2:具体原型 - 游戏角色 ==================== /** * 技能类(引用类型,演示深拷贝必要性) */ class Skill { public name: string; public damage: number; public cooldown: number; constructor(name: string, damage: number, cooldown: number) { this.name = name; this.damage = damage; this.cooldown = cooldown; } // 克隆技能 clone(): Skill { return new Skill(this.name, this.damage, this.cooldown); } toString(): string { return `${this.name}(伤害:${this.damage}, CD:${this.cooldown}s)`; } } /** * 装备类(引用类型) */ class Equipment { public weapon: string; public armor: string; public accessory: string; constructor(weapon: string, armor: string, accessory: string) { this.weapon = weapon; this.armor = armor; this.accessory = accessory; } clone(): Equipment { return new Equipment(this.weapon, this.armor, this.accessory); } toString(): string { return `武器:${this.weapon}, 防具:${this.armor}, 饰品:${this.accessory}`; } } /** * 具体原型:游戏角色 * 演示浅拷贝和深拷贝的区别 */ class GameCharacter implements ICloneable<GameCharacter> { // 基本类型(值类型) public name: string; public level: number; public health: number; public mana: number; // 引用类型(需要深拷贝) public skills: Skill[]; // 技能列表 public equipment: Equipment; // 装备 public buffs: Map<string, number>; // 增益效果(持续回合数) // 私有字段 private characterId: number; private static nextId: number = 1000; constructor(name: string, level: number = 1) { this.name = name; this.level = level; this.health = 100 + level * 10; this.mana = 50 + level * 5; this.skills = []; this.equipment = new Equipment("新手剑", "布甲", "木项链"); this.buffs = new Map(); this.characterId = GameCharacter.nextId++; console.log(` 🎮 创建新角色: ${this.name} (ID: ${this.characterId}, 构造函数被调用)`); } /** * 添加技能 */ addSkill(skill: Skill): void { this.skills.push(skill); } /** * 添加增益效果 */ addBuff(buffName: string, duration: number): void { this.buffs.set(buffName, duration); } /** * 浅拷贝克隆 * 注意:引用类型(skills、equipment、buffs)会被共享 */ clone(): GameCharacter { const cloned = new GameCharacter(this.name, this.level); // 浅拷贝:直接复制引用 cloned.health = this.health; cloned.mana = this.mana; cloned.skills = this.skills; // ⚠️ 共享引用 cloned.equipment = this.equipment; // ⚠️ 共享引用 cloned.buffs = this.buffs; // ⚠️ 共享引用 return cloned; } /** * 深拷贝克隆 * 递归复制所有引用类型 */ deepClone(): GameCharacter { console.log(` 🔄 深拷贝角色: ${this.name}`); const cloned = new GameCharacter(this.name, this.level); cloned.health = this.health; cloned.mana = this.mana; // 深拷贝技能列表 cloned.skills = this.skills.map(skill => skill.clone()); // 深拷贝装备 cloned.equipment = this.equipment.clone(); // 深拷贝增益效果Map cloned.buffs = new Map(this.buffs); return cloned; } /** * 展示角色信息 */ display(): void { console.log(`\n 👤 角色: ${this.name} (ID: ${this.characterId})`); console.log(` 等级: ${this.level}, HP: ${this.health}, MP: ${this.mana}`); console.log(` 装备: ${this.equipment.toString()}`); console.log(` 技能: ${this.skills.map(s => s.toString()).join(', ')}`); if (this.buffs.size > 0) { console.log(` 增益: ${Array.from(this.buffs.entries()).map(([k, v]) => `${k}(${v}回合)`).join(', ')}`); } } /** * 修改角色属性(演示原型独立性) */ setEquipment(weapon: string, armor: string, accessory: string): void { this.equipment = new Equipment(weapon, armor, accessory); } addBuffTest(buffName: string): void { this.buffs.set(buffName, 3); } } // ==================== 场景3:怪物生成器(原型管理器) ==================== /** * 原型管理器(注册表) * 管理所有可用的原型对象 */ class MonsterPrototypeManager { private static prototypes: Map<string, ICloneable<Monster>> = new Map(); static registerMonster(type: string, prototype: Monster): void { this.prototypes.set(type, prototype); console.log(` 📝 注册怪物原型: ${type}`); } static createMonster(type: string): Monster | null { const prototype = this.prototypes.get(type); if (!prototype) { console.log(` ❌ 未找到怪物类型: ${type}`); return null; } console.log(` 🎯 从原型创建怪物: ${type}`); return prototype.deepClone(); } static getAllTypes(): string[] { return Array.from(this.prototypes.keys()); } } /** * 怪物类 */ class Monster implements ICloneable<Monster> { public type: string; public name: string; public level: number; public health: number; public attack: number; public skills: string[]; public lootTable: string[]; constructor(type: string, name: string, level: number) { this.type = type; this.name = name; this.level = level; this.health = 50 * level; this.attack = 10 * level; this.skills = []; this.lootTable = []; console.log(` 👾 创建新怪物: ${this.name} (构造函数调用)`); } addSkill(skill: string): void { this.skills.push(skill); } addLoot(item: string): void { this.lootTable.push(item); } clone(): Monster { const cloned = new Monster(this.type, this.name, this.level); cloned.health = this.health; cloned.attack = this.attack; cloned.skills = [...this.skills]; cloned.lootTable = [...this.lootTable]; return cloned; } deepClone(): Monster { // 对于Monster,浅拷贝就够了(没有深层引用) return this.clone(); } display(): void { console.log(` 👾 ${this.name} (Lv.${this.level})`); console.log(` HP: ${this.health}, ATK: ${this.attack}`); console.log(` 技能: ${this.skills.join(', ')}`); console.log(` 掉落: ${this.lootTable.join(', ')}`); } } // ==================== 客户端代码 ==================== /** * 客户端1:游戏管理器 */ class GameManager { private characterCache: Map<string, GameCharacter> = new Map(); /** * 缓存角色原型 */ cacheCharacter(key: string, character: GameCharacter): void { this.characterCache.set(key, character); console.log(`💾 缓存角色原型: ${key}`); } /** * 从缓存创建角色(使用原型模式) */ createFromCache(key: string): GameCharacter | null { const prototype = this.characterCache.get(key); if (!prototype) { console.log(`❌ 未找到缓存: ${key}`); return null; } console.log(`✨ 从缓存克隆角色: ${key}`); return prototype.deepClone(); } /** * 批量创建克隆怪物(性能演示) */ spawnMonsterWave(prototype: Monster, count: number): Monster[] { console.log(`\n 🎲 生成 ${count} 只怪物军团...`); const startTime = Date.now(); const monsters: Monster[] = []; for (let i = 0; i < count; i++) { // 使用克隆,避免重复初始化 const monster = prototype.deepClone(); monster.name = `${prototype.name}#${i + 1}`; monsters.push(monster); } const endTime = Date.now(); console.log(` ✅ 生成完成,耗时: ${endTime - startTime}ms`); return monsters; } } // ==================== 程序入口 ==================== function main() { console.log("=" .repeat(70)); console.log("原型模式演示 - 游戏角色克隆系统"); console.log("=" .repeat(70)); // 演示1:基础克隆(浅拷贝 vs 深拷贝) console.log("\n【演示1:浅拷贝 vs 深拷贝的区别】"); // 创建原始角色 console.log("\n1. 创建原始角色:"); const original = new GameCharacter("战士", 10); original.addSkill(new Skill("旋风斩", 150, 6)); original.addSkill(new Skill("嘲讽", 0, 10)); original.setEquipment("巨剑", "板甲", "力量戒指"); original.addBuffTest("力量祝福"); original.display(); // 浅拷贝克隆 console.log("\n2. 浅拷贝克隆:"); const shallowCopy = original.clone(); shallowCopy.display(); // 修改浅拷贝的引用类型属性 console.log("\n3. 修改浅拷贝的装备和技能:"); shallowCopy.setEquipment("匕首", "皮甲", "敏捷项链"); shallowCopy.skills[0].damage = 999; // 修改技能伤害 console.log("\n 原始角色(受影响):"); original.display(); console.log("\n 浅拷贝角色:"); shallowCopy.display(); // 深拷贝克隆 console.log("\n4. 深拷贝克隆(不受影响):"); const deepCopy = original.deepClone(); deepCopy.display(); // 演示3:原型管理器(怪物生成) console.log("\n" + "=" .repeat(70)); console.log("【演示3:原型管理器 - 怪物生成系统】"); // 创建怪物原型 const goblinPrototype = new Monster("goblin", "哥布林", 5); goblinPrototype.addSkill("偷窃"); goblinPrototype.addSkill("投掷石子"); goblinPrototype.addLoot("铜币"); goblinPrototype.addLoot("皮革"); const dragonPrototype = new Monster("dragon", "幼龙", 20); dragonPrototype.addSkill("火焰吐息"); dragonPrototype.addSkill("龙爪攻击"); dragonPrototype.addLoot("龙鳞"); dragonPrototype.addLoot("龙晶"); // 注册原型 MonsterPrototypeManager.registerMonster("goblin", goblinPrototype); MonsterPrototypeManager.registerMonster("dragon", dragonPrototype); // 从原型创建怪物 const goblin1 = MonsterPrototypeManager.createMonster("goblin"); const goblin2 = MonsterPrototypeManager.createMonster("goblin"); const dragon1 = MonsterPrototypeManager.createMonster("dragon"); if (goblin1) goblin1.display(); if (goblin2) goblin2.display(); if (dragon1) dragon1.display(); // 演示4:批量克隆性能对比 console.log("\n" + "=" .repeat(70)); console.log("【演示4:性能对比 - 克隆 vs 直接创建】"); const gameManager = new GameManager(); // 方式1:使用原型克隆 console.log("\n方式1:使用原型克隆创建100个怪物"); const prototype = new Monster("orc", "兽人战士", 10); prototype.addSkill("狂暴"); prototype.addLoot("兽人牙齿"); const cloneStart = Date.now(); const clonedMonsters: Monster[] = []; for (let i = 0; i < 100; i++) { const monster = prototype.deepClone(); monster.name = `兽人#${i}`; clonedMonsters.push(monster); } const cloneEnd = Date.now(); console.log(`✅ 克隆方式耗时: ${cloneEnd - cloneStart}ms`); // 方式2:直接使用new创建 console.log("\n方式2:直接new创建100个怪物"); const newStart = Date.now(); const newMonsters: Monster[] = []; for (let i = 0; i < 100; i++) { const monster = new Monster("orc", `兽人#${i}`, 10); monster.addSkill("狂暴"); monster.addLoot("兽人牙齿"); newMonsters.push(monster); } const newEnd = Date.now(); console.log(`✅ new方式耗时: ${newEnd - newStart}ms`); console.log(`\n📊 性能提升: ${((newEnd - newStart) - (cloneEnd - cloneStart))}ms (${(((newEnd - newStart) - (cloneEnd - cloneStart)) / (newEnd - newStart) * 100).toFixed(1)}% 更快)`); // 演示5:运行时动态克隆与修改 console.log("\n" + "=" .repeat(70)); console.log("【演示5:运行时动态配置 - 英雄变体】"); const baseHero = new GameCharacter("圣骑士", 15); baseHero.addSkill(new Skill("圣光术", 200, 8)); baseHero.addSkill(new Skill("奉献", 80, 12)); baseHero.setEquipment("圣剑", "圣盾", "圣契"); // 创建不同变体(无需重新new) const damageHero = baseHero.deepClone(); damageHero.name = "惩戒骑士"; damageHero.setEquipment("大领主之剑", "板甲", "伤害徽记"); const tankHero = baseHero.deepClone(); tankHero.name = "防护骑士"; tankHero.setEquipment("单手锤", "防御者之盾", "耐力饰品"); tankHero.addSkill(new Skill("盾牌格挡", 0, 5)); const healHero = baseHero.deepClone(); healHero.name = "神圣骑士"; healHero.setEquipment("治疗法杖", "布甲", "智力戒指"); healHero.addSkill(new Skill("神圣震击", 150, 6)); console.log("\n基于原型生成的骑士变体:"); damageHero.display(); tankHero.display(); healHero.display(); // 总结 console.log("\n" + "=" .repeat(70)); console.log("【原型模式核心要点】"); console.log("✅ 避免昂贵初始化:克隆比new+初始化更高效"); console.log("✅ 保留对象状态:可以复制对象任意时刻的快照"); console.log("✅ 动态配置:运行时从原型创建新变体"); console.log("✅ 减少子类:无需为每种配置创建子类"); console.log("⚠️ 注意深浅拷贝:根据需求选择合适的克隆方式"); console.log("=" .repeat(70)); } // 运行 main();
单例
单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点来获取该实例。
-
通俗理解
1.任务管理器:Windows系统只能打开一个任务管理器窗口
2.配置文件:整个应用共享同一个配置对象
-
核心角色
角色 英文名 职责 本示例对应 单例类 Singleton 私有构造函数 + 静态方法返回唯一实例 ConfigManager、DatabaseConnection 客户端 Client 通过全局访问点获取单例实例 Main 函数 单例模式的工作流程
text客户端A ──┐ ├──→ getInstance() ──→ 返回同一个实例 客户端B ──┘ -
优缺点分析
✅ 优点
优点 说明 节省资源 避免重复创建对象,减轻内存和GC压力 全局状态一致 确保所有模块访问同一状态 延迟加载 只有在第一次使用时才创建实例 简化访问 无需传递实例引用,全局可直接访问 ❌ 缺点
缺点 说明 解决方案 全局状态问题 隐藏依赖关系,增加耦合 结合依赖注入容器使用 并发问题 多线程可能创建多个实例 使用双重检查锁或静态初始化 测试困难 单例状态在测试间会互相干扰 提供重置方法或使用Mock框架 违反单一职责 既管理实例又负责业务逻辑 考虑使用工厂模式管理 -
应用场景
场景类型 具体描述 真实案例 配置管理 整个应用共享配置信息 数据库配置、API密钥 日志记录 统一的日志输出对象 文件日志、数据库日志 连接池 数据库连接池、线程池 MySQL连接池 缓存管理 全局缓存对象 Redis客户端、内存缓存 设备驱动 打印机、扫描仪等硬件控制 打印机池管理 状态管理 Vuex/Pinia/Redux Store 应用全局状态 -
应用示例:全局配置管理器 + 数据库连接池
typescript// ==================== 角色1:单例类 - 配置管理器 ==================== /** * 配置管理器(饿汉式单例 - 类加载时创建) * 适合:配置信息、资源消耗小的对象 */ class ConfigManager { private static instance: ConfigManager = new ConfigManager(); private configs: Map<string, string> = new Map(); // 1. 私有构造函数:阻止外部 new private constructor() { console.log("🔧 ConfigManager 实例已创建"); // 模拟加载配置文件 this.configs.set("app.name", "MyApp"); this.configs.set("app.version", "1.0.0"); this.configs.set("db.host", "localhost"); this.configs.set("db.port", "5432"); } // 2. 静态方法:提供全局访问点 static getInstance(): ConfigManager { return ConfigManager.instance; } get(key: string): string | undefined { return this.configs.get(key); } set(key: string, value: string): void { console.log(`📝 配置更新: ${key} = ${value}`); this.configs.set(key, value); } getAll(): Map<string, string> { return new Map(this.configs); // 返回副本,避免外部修改 } } // ==================== 角色2:单例类 - 数据库连接池 ==================== /** * 数据库连接池(懒汉式单例 - 延迟加载 + 双重检查锁) * 适合:创建成本高的对象 */ class DatabaseConnection { private static instance: DatabaseConnection | null = null; private static lock: boolean = false; private connectionCount: number = 0; private isConnected: boolean = false; private constructor() { console.log("🏗️ DatabaseConnection 构造函数调用"); } // 双重检查锁实现线程安全(TS单线程模拟,展示结构) static getInstance(): DatabaseConnection { // 第一次检查:避免进入同步块 if (DatabaseConnection.instance === null) { // 模拟锁(在真实多线程环境用互斥锁) console.log(" 🔒 获取锁,创建实例..."); // 第二次检查:防止多线程重复创建 if (DatabaseConnection.instance === null) { DatabaseConnection.instance = new DatabaseConnection(); DatabaseConnection.instance.connect(); } console.log(" 🔓 释放锁"); } return DatabaseConnection.instance; } private connect(): void { if (!this.isConnected) { this.isConnected = true; this.connectionCount = 5; console.log(`✅ 数据库连接成功,连接池大小: ${this.connectionCount}`); } } query(sql: string): void { if (!this.isConnected) { console.log("❌ 数据库未连接"); return; } console.log(`💾 执行查询: ${sql.substring(0, 50)}...`); } getConnectionCount(): number { return this.connectionCount; } // 测试辅助:重置单例(生产环境慎用) static reset(): void { DatabaseConnection.instance = null; console.log("🔄 连接池已重置"); } } // ==================== 客户端代码 ==================== function main() { console.log("=".repeat(60)); console.log("单例模式演示"); console.log("=".repeat(60)); // 演示1:配置管理器(饿汉式) console.log("\n【演示1:配置管理器】"); const config1 = ConfigManager.getInstance(); const config2 = ConfigManager.getInstance(); console.log(`配置1 === 配置2: ${config1 === config2}`); console.log(`应用名称: ${config1.get("app.name")}`); console.log(`数据库端口: ${config1.get("db.port")}`); config1.set("app.env", "production"); console.log(`从 config2 读取新配置: ${config2.get("app.env")}`); // 演示2:数据库连接池(懒汉式) console.log("\n【演示2:数据库连接池】"); const db1 = DatabaseConnection.getInstance(); const db2 = DatabaseConnection.getInstance(); console.log(`db1 === db2: ${db1 === db2}`); console.log(`连接池大小: ${db1.getConnectionCount()}`); db1.query("SELECT * FROM users"); // 演示4:单例验证 console.log("\n【演示4:单例验证】"); console.log(`✅ ConfigManager: ${config1 === config2 ? "同一实例" : "不同实例"}`); console.log(`✅ DatabaseConnection: ${db1 === db2 ? "同一实例" : "不同实例"}`); // 演示5:懒加载时机验证 console.log("\n【演示5:懒加载验证】"); console.log("此时 DatabaseConnection 尚未创建"); // 只有在第一次调用 getInstance 时才会创建 setTimeout(() => { console.log("\n⏰ 延迟2秒后首次调用 getInstance():"); const db3 = DatabaseConnection.getInstance(); console.log(`✅ 实例创建成功: ${db3.getConnectionCount()} 个连接`); }, 2000); } // 运行演示 main(); // 导出供其他模块使用(生产环境使用) //export { ConfigManager, DatabaseConnection };
构建器
构建器模式(Builder Pattern)将一个复杂对象的构造过程与表示分离,使得同样的构建过程可以创建不同的表示。
-
通俗理解
组装电脑:相同的步骤(选CPU→选内存→选硬盘→选显卡)可以组装出不同配置的电脑
-
核心角色
角色 英文名 职责 本示例对应 产品 Product 被构建的复杂对象 Computer 构建器接口 Builder 声明构建步骤的抽象接口 ComputerBuilder 具体构建器 Concrete Builder 实现构建步骤,管理产品 GamingComputerBuilder、OfficeComputerBuilder 指挥者 Director 定义构建顺序,封装构建逻辑 ComputerAssembler 构建器模式的工作流程
text客户端 → 指挥者(Director) → 构建器(Builder) → 产品(Product) ↓ ↓ 定义构建顺序 执行具体构建步骤 -
优缺点分析
✅ 优点
优点 说明 分步构建 可以将复杂对象的构建拆解为多个简单步骤 复用构建过程 相同的构建步骤可以创建不同的表示 参数灵活 避免 telescoping constructor(参数过多的构造函数) 不可变性 可以构建不可变对象(构建完成后不可修改) 代码可读性 链式调用让代码更清晰易读 ❌ 缺点
缺点 说明 解决方案 代码冗余 每个产品都需要对应的构建器 仅在复杂对象时使用 增加复杂度 引入多个新类 使用内部静态构建器类 对简单对象过度设计 对象属性少时没必要 直接使用构造函数或对象字面量 -
应用场景
场景类型 具体描述 真实案例 对象参数众多 超过4个可选参数,且有默认值 数据库连接配置 构建过程复杂 需要验证、依赖检查等步骤 订单对象创建 多种表示 同一步骤可以生成不同对象 文档生成器(PDF/HTML) 不可变对象 对象创建后不可修改 配置对象、值对象 链式调用 需要流畅的API风格 Query Builder、URL Builder -
应用示例:电脑配置构建器
typescript// ==================== 角色1:产品 ==================== /** * 电脑产品类 */ class Computer { // 必需属性 private cpu: string; private ram: string; private storage: string; // 可选属性 private gpu?: string; private motherboard?: string; private powerSupply?: string; private cooling?: string; constructor(cpu: string, ram: string, storage: string) { this.cpu = cpu; this.ram = ram; this.storage = storage; } // 使用链式设置方法(内部使用,由Builder调用) setGpu(gpu: string): this { this.gpu = gpu; return this; } setMotherboard(mb: string): this { this.motherboard = mb; return this; } setPowerSupply(ps: string): this { this.powerSupply = ps; return this; } setCooling(cooling: string): this { this.cooling = cooling; return this; } display(): void { console.log("\n💻 电脑配置:"); console.log(` CPU: ${this.cpu}`); console.log(` 内存: ${this.ram}`); console.log(` 硬盘: ${this.storage}`); this.gpu && console.log(` 显卡: ${this.gpu}`); this.motherboard && console.log(` 主板: ${this.motherboard}`); this.powerSupply && console.log(` 电源: ${this.powerSupply}`); this.cooling && console.log(` 散热: ${this.cooling}`); } } // ==================== 角色2:构建器接口 ==================== interface ComputerBuilder { setGPU(gpu: string): this; setMotherboard(mb: string): this; setPowerSupply(ps: string): this; setCooling(cooling: string): this; build(): Computer; } // ==================== 角色3:具体构建器 ==================== class GamingComputerBuilder implements ComputerBuilder { private computer: Computer; constructor() { this.computer = new Computer("Intel i9-13900K", "32GB DDR5", "1TB NVMe SSD"); } setGPU(gpu: string): this { this.computer.setGpu(gpu); return this; } setMotherboard(mb: string): this { this.computer.setMotherboard(mb); return this; } setPowerSupply(ps: string): this { this.computer.setPowerSupply(ps); return this; } setCooling(cooling: string): this { this.computer.setCooling(cooling); return this; } build(): Computer { return this.computer; } } class OfficeComputerBuilder implements ComputerBuilder { private computer: Computer; constructor() { this.computer = new Computer("Intel i5-13400", "16GB DDR4", "512GB SSD"); } setGPU(gpu: string): this { this.computer.setGpu(gpu); return this; } setMotherboard(mb: string): this { this.computer.setMotherboard(mb); return this; } setPowerSupply(ps: string): this { this.computer.setPowerSupply(ps); return this; } setCooling(cooling: string): this { this.computer.setCooling(cooling); return this; } build(): Computer { return this.computer; } } // ==================== 角色4:指挥者 ==================== class ComputerAssembler { private builder: ComputerBuilder; constructor(builder: ComputerBuilder) { this.builder = builder; } assembleGamingPC(): Computer { return this.builder .setGPU("NVIDIA RTX 4090") .setMotherboard("ASUS ROG Maximus") .setPowerSupply("1000W 金牌") .setCooling("360mm 水冷") .build(); } assembleOfficePC(): Computer { return this.builder .setGPU("集成显卡") .setMotherboard("B760M") .setPowerSupply("500W 铜牌") .build(); } } // ==================== 客户端代码 ==================== function main() { console.log("=".repeat(60)); console.log("构建器模式演示 - 电脑配置系统"); console.log("=".repeat(60)); // 游戏电脑 const gamingBuilder = new GamingComputerBuilder(); const assembler = new ComputerAssembler(gamingBuilder); const gamingPC = assembler.assembleGamingPC(); gamingPC.display(); // 办公电脑(复用同一个构建过程) const officeBuilder = new OfficeComputerBuilder(); const officeAssembler = new ComputerAssembler(officeBuilder); const officePC = officeAssembler.assembleOfficePC(); officePC.display(); } // 运行 main();
结构型模式
关注类/对象组合:这些模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。
| 模式 | 核心作用 |
|---|---|
| 适配器 | 接口转换兼容 |
| 装饰器 | 动态添加职责 |
| 代理 | 控制对象访问 |
| 外观 | 简化复杂子系统 |
| 桥接 | 抽象与实现分离 |
| 组合 | 树形结构统一处理 |
| 享元 | 共享细粒度对象 |
适配器
适配器模式(Adapter Pattern)充当两个不兼容接口之间的桥梁,将一个类的接口转换成客户端期望的另一个接口,使原本因接口不匹配而无法一起工作的类能够协同工作。
-
通俗理解
电源适配器:将220V交流电转换为手机需要的5V直流电
-
核心角色
角色 英文名 职责 本示例对应 目标接口 Target 客户端期望使用的接口 IUserService 适配者 Adaptee 需要被适配的现有类 LegacyUserSystem 适配器 Adapter 将适配者接口转换为目标接口 UserAdapter 适配器模式的工作流程
typescript客户端 → 目标接口 ← 适配器 ← 适配者 ↓ 转换调用 -
优缺点分析
✅ 优点
优点 说明 兼容性 让不兼容的类可以协同工作 复用性 复用现有的功能类,无需修改原有代码 透明性 客户端只与目标接口交互,不知道适配器存在 灵活性 可以在不修改原有系统的情况下引入新功能 ❌ 缺点
缺点 说明 解决方案 增加复杂度 引入新类,增加系统复杂度 只在必要时使用 过度适配 适配器过多导致系统混乱 重构统一接口而非反复适配 -
应用场景
场景类型 具体描述 真实案例 系统升级 新系统需要兼容旧系统的接口 日志框架适配 第三方库集成 封装第三方库的复杂接口 支付网关适配 接口标准化 将不同接口统一为标准接口 数据格式转换 遗留系统改造 不改旧代码,用适配器对接新接口 老系统API适配 -
应用示例:用户服务升级适配
typescript// ==================== 角色1:目标接口 ==================== // 新系统期望的用户服务接口 interface IUserService { getUserById(id: string): { id: string; name: string; email: string }; saveUser(name: string, email: string): void; } // ==================== 角色2:适配者 ==================== // 遗留的老用户系统(接口不兼容) class LegacyUserSystem { // 老方法:使用数字ID,返回格式不同 fetchUser(userId: number): any { return { userId: userId, fullName: "User_" + userId, mail: `user${userId}@old.com` }; } // 老方法:插入返回布尔值 insertUser(userData: any): boolean { console.log(` 老系统保存用户: ${userData.fullName}`); return true; } } // ==================== 角色3:适配器 ==================== // 适配器:将老系统接口转换为新接口 class UserAdapter implements IUserService { private legacySystem: LegacyUserSystem; constructor(legacySystem: LegacyUserSystem) { this.legacySystem = legacySystem; } getUserById(id: string): { id: string; name: string; email: string } { // 转换参数:string → number const numericId = parseInt(id); // 调用老系统方法 const oldUser = this.legacySystem.fetchUser(numericId); // 转换返回格式:老格式 → 新格式 return { id: oldUser.userId.toString(), name: oldUser.fullName, email: oldUser.mail }; } saveUser(name: string, email: string): void { // 转换参数:新格式 → 老格式 const oldFormatData = { fullName: name, mail: email }; // 调用老系统方法(忽略返回值) this.legacySystem.insertUser(oldFormatData); } } // ==================== 客户端 ==================== // 新系统只依赖 IUserService 接口 class UserController { private userService: IUserService; constructor(userService: IUserService) { this.userService = userService; } showUser(id: string): void { const user = this.userService.getUserById(id); console.log(`\n📋 用户信息:`); console.log(` ID: ${user.id}`); console.log(` 姓名: ${user.name}`); console.log(` 邮箱: ${user.email}`); } createUser(name: string, email: string): void { console.log(`\n📝 创建用户: ${name}`); this.userService.saveUser(name, email); } } // ==================== 程序入口 ==================== function main() { console.log("=".repeat(60)); console.log("适配器模式演示 - 用户服务升级"); console.log("=".repeat(60)); // 老系统实例 const legacySystem = new LegacyUserSystem(); // 适配器(将老系统包装成新接口) const adapter = new UserAdapter(legacySystem); // 客户端使用新接口(不知道底层是老系统) const controller = new UserController(adapter); // 查询用户(参数自动转换) controller.showUser("123"); // 创建用户(调用自动适配) controller.createUser("张三", "zhang@example.com"); // 验证:适配器透明性 console.log("\n✅ 客户端只依赖 IUserService,不知道底层是 LegacyUserSystem"); } main();
桥接
桥接模式(Bridge Pattern)将抽象部分与实现部分分离,使它们可以独立变化。通过组合关系替代继承关系,避免类爆炸问题。
-
通俗理解
1.遥控器与电视:遥控器(抽象)和电视品牌(实现)可以独立变化,同一遥控器可控制不同品牌电视
2.颜色与形状:形状(圆形、方形)和颜色(红色、蓝色)可以独立组合,避免 2×2=4 个类
-
核心角色
角色 英文名 职责 本示例对应 抽象 Abstraction 定义抽象接口,维护对实现的引用 RemoteControl 扩展抽象 Refined Abstraction 扩展抽象接口,增加新功能 AdvancedRemoteControl 实现 Implementor 定义实现接口,提供基本操作 Device 具体实现 Concrete Implementor 实现具体业务逻辑 TV、Radio 桥接模式的结构
text抽象层 (RemoteControl) ──桥接──→ 实现层 (Device) ↓ ↓ 扩展抽象 (AdvancedRemote) 具体实现 (TV, Radio) -
优缺点分析
✅ 优点
优点 说明 避免类爆炸 用组合替代继承,M×N 种组合只需 M+N 个类 单一职责 抽象和实现各司其职,互不干扰 开闭原则 新增抽象或实现都无需修改现有代码 透明性 客户端只关心抽象接口,不关心实现细节 扩展灵活 抽象和实现可独立扩展 ❌ 缺点
缺点 说明 解决方案 增加复杂度 引入两个独立层次结构 仅在多维度变化时使用 设计难度 需要准确识别两个变化维度 充分分析需求变化点 -
应用场景
场景类型 具体描述 真实案例 多维度变化 类在两个独立维度上变化 形状×颜色、设备×遥控器 避免继承爆炸 M×N 组合导致子类过多 跨平台UI组件 运行时切换 需要在运行时动态切换实现 数据库驱动、消息推送 共享实现 多个对象共享同一实现 连接池、缓存实现 -
应用示例:设备遥控器系统
typescript// ==================== 角色1:实现接口 ==================== // 设备接口(实现层) interface Device { isEnabled(): boolean; enable(): void; disable(): void; getVolume(): number; setVolume(percent: number): void; getChannel(): number; setChannel(channel: number): void; getDeviceName(): string; } // ==================== 角色2:具体实现 ==================== // 电视机实现 class TV implements Device { private on: boolean = false; private volume: number = 30; private channel: number = 1; isEnabled(): boolean { return this.on; } enable(): void { this.on = true; console.log(` 📺 TV已开机`); } disable(): void { this.on = false; console.log(` 📺 TV已关机`); } getVolume(): number { return this.volume; } setVolume(percent: number): void { this.volume = Math.min(100, Math.max(0, percent)); console.log(` 📺 TV音量: ${this.volume}%`); } getChannel(): number { return this.channel; } setChannel(channel: number): void { this.channel = channel; console.log(` 📺 TV频道: ${this.channel}`); } getDeviceName(): string { return "电视机"; } } // 收音机实现 class Radio implements Device { private on: boolean = false; private volume: number = 20; private channel: number = 88.5; isEnabled(): boolean { return this.on; } enable(): void { this.on = true; console.log(` 📻 收音机已开机`); } disable(): void { this.on = false; console.log(` 📻 收音机已关机`); } getVolume(): number { return this.volume; } setVolume(percent: number): void { this.volume = Math.min(100, Math.max(0, percent)); console.log(` 📻 收音机音量: ${this.volume}%`); } getChannel(): number { return this.channel; } setChannel(channel: number): void { this.channel = channel; console.log(` 📻 收音机频率: ${this.channel}MHz`); } getDeviceName(): string { return "收音机"; } } // ==================== 角色3:抽象 ==================== // 遥控器抽象类 class RemoteControl { protected device: Device; constructor(device: Device) { this.device = device; } togglePower(): void { if (this.device.isEnabled()) { this.device.disable(); } else { this.device.enable(); } } volumeUp(): void { this.device.setVolume(this.device.getVolume() + 10); } volumeDown(): void { this.device.setVolume(this.device.getVolume() - 10); } channelUp(): void { this.device.setChannel(this.device.getChannel() + 1); } channelDown(): void { this.device.setChannel(this.device.getChannel() - 1); } } // ==================== 角色4:扩展抽象 ==================== // 高级遥控器(增加静音、收藏频道等功能) class AdvancedRemoteControl extends RemoteControl { mute(): void { this.device.setVolume(0); console.log(` 🔇 ${this.device.getDeviceName()}已静音`); } getDeviceInfo(): void { console.log(`\n 📱 当前设备: ${this.device.getDeviceName()}`); console.log(` 状态: ${this.device.isEnabled() ? "开机" : "关机"}`); console.log(` 音量: ${this.device.getVolume()}%`); console.log(` 频道: ${this.device.getChannel()}`); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("桥接模式演示 - 设备遥控器系统"); console.log("=".repeat(60)); // 组合1:基础遥控器 + 电视机 console.log("\n【基础遥控器控制电视机】"); const tv = new TV(); const basicRemote = new RemoteControl(tv); basicRemote.togglePower(); // 开机 basicRemote.volumeUp(); // 音量+10 basicRemote.channelUp(); // 频道+1 // 组合2:高级遥控器 + 收音机 console.log("\n【高级遥控器控制收音机】"); const radio = new Radio(); const advancedRemote = new AdvancedRemoteControl(radio); advancedRemote.togglePower(); // 开机 advancedRemote.volumeUp(); // 音量+10 advancedRemote.channelUp(); // 频率+1 advancedRemote.getDeviceInfo(); // 查看状态 advancedRemote.mute(); // 静音 // 运行时切换设备(桥接模式的灵活性) console.log("\n【运行时切换设备】"); const smartRemote = new AdvancedRemoteControl(tv); smartRemote.getDeviceInfo(); // 控制电视机 smartRemote.togglePower(); console.log("\n 🔄 切换设备到收音机..."); (smartRemote as any).device = radio; // 动态切换实现 smartRemote.getDeviceInfo(); // 现在控制收音机 smartRemote.togglePower(); console.log("\n✅ 桥接模式核心:抽象与实现分离,两者可独立扩展"); } main();
组合
组合模式(Composite Pattern)将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得客户端对单个对象和组合对象的访问具有一致性,无需区分叶子节点和容器节点。
-
通俗理解
文件系统:文件和文件夹都可以进行复制、移动、删除操作
-
核心角色
角色 英文名 职责 本示例对应 组件 Component 声明公共接口,实现默认行为 FileSystemNode 叶子节点 Leaf 表示叶子对象,没有子节点 File 容器节点 Composite 表示容器对象,存储子节点 Folder 组合模式的结构
textComponent (接口) / \ Leaf Composite (文件) (文件夹) / 子节点们 -
优缺点分析
✅ 优点
优点 说明 统一接口 客户端无需区分叶子节点和容器节点 递归结构 天然支持树形结构的递归操作 开闭原则 新增组件类型无需修改现有代码 灵活性 可以轻松构建复杂的树形结构 ❌ 缺点
缺点 说明 解决方案 类型限制 所有组件必须实现相同接口 使用接口默认方法或类型检查 过度泛化 叶子节点可能实现无效方法 提供默认实现或抛出异常 性能开销 深层次递归可能影响性能 使用缓存或延迟加载 -
应用场景
场景类型 具体描述 真实案例 树形结构 需要表示部分-整体层次关系 文件系统、组织架构 统一处理 希望统一对待单个对象和组合对象 UI组件树、菜单系统 递归操作 需要对整个树结构执行递归操作 计算总价、统计节点数 动态结构 结构在运行时可以动态改变 购物车、文档结构 -
应用示例:文件系统
typescript// ==================== 角色1:组件接口 ==================== // 文件系统节点接口 interface FileSystemNode { getName(): string; getSize(): number; display(indent?: string): void; add?(node: FileSystemNode): void; remove?(node: FileSystemNode): void; getChild?(index: number): FileSystemNode; } // ==================== 角色2:叶子节点 ==================== // 文件类(叶子节点) class File implements FileSystemNode { private name: string; private size: number; constructor(name: string, size: number) { this.name = name; this.size = size; } getName(): string { return this.name; } getSize(): number { return this.size; } display(indent: string = ""): void { console.log(`${indent}📄 ${this.name} (${this.size}KB)`); } } // ==================== 角色3:容器节点 ==================== // 文件夹类(容器节点) class Folder implements FileSystemNode { private name: string; private children: FileSystemNode[] = []; constructor(name: string) { this.name = name; } getName(): string { return this.name; } getSize(): number { // 递归计算所有子节点大小总和 return this.children.reduce((total, child) => total + child.getSize(), 0); } add(node: FileSystemNode): void { this.children.push(node); console.log(` 📁 ${this.name} 添加了: ${node.getName()}`); } remove(node: FileSystemNode): void { const index = this.children.indexOf(node); if (index !== -1) { this.children.splice(index, 1); console.log(` 📁 ${this.name} 移除了: ${node.getName()}`); } } getChild(index: number): FileSystemNode { return this.children[index]; } display(indent: string = ""): void { console.log(`${indent}📁 ${this.name}/ (${this.getSize()}KB)`); // 递归显示所有子节点 this.children.forEach(child => { child.display(indent + " "); }); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("组合模式演示 - 文件系统"); console.log("=".repeat(60)); // 创建文件节点 const file1 = new File("简历.pdf", 120); const file2 = new File("照片.jpg", 2500); const file3 = new File("笔记.txt", 5); const file4 = new File("视频.mp4", 15000); // 创建文件夹(容器) const root = new Folder("我的文档"); const docs = new Folder("工作文档"); const images = new Folder("图片"); const videos = new Folder("视频"); // 构建树形结构 console.log("\n【构建文件树】"); root.add(docs); root.add(images); root.add(videos); docs.add(file1); docs.add(file3); images.add(file2); videos.add(file4); // 显示整个目录结构 console.log("\n【目录结构】"); root.display(); // 统一操作:计算总大小 console.log("\n【统一操作】"); console.log(`总大小: ${root.getSize()}KB`); console.log(`图片文件夹大小: ${images.getSize()}KB`); // 叶子节点操作(与容器节点接口一致) console.log("\n【叶子节点操作】"); console.log(`文件: ${file1.getName()}, 大小: ${file1.getSize()}KB`); // 动态添加/删除 console.log("\n【动态操作】"); const temp = new File("临时文件.tmp", 10); docs.add(temp); docs.remove(temp); // 递归遍历演示 console.log("\n【递归遍历 - 手动遍历】"); console.log(`根目录共有 ${countNodes(root)} 个节点`); console.log("\n✅ 组合模式核心:统一处理单个对象和组合对象"); } // 辅助函数:递归统计节点数(演示统一接口) function countNodes(node: FileSystemNode): number { let count = 1; // 如果是容器节点,递归统计子节点 if (node.getChild) { let i = 0; while (node.getChild(i)) { count += countNodes(node.getChild(i)!); i++; } } return count; } main();
装饰
装饰模式(Decorator Pattern)允许动态地给一个对象添加额外的职责,而不需要修改原始类的代码。通过使用装饰器,可以在运行时扩展对象的功能,比继承更加灵活。
-
通俗理解
咖啡点单:基础咖啡(浓缩、美式)+ 调料(牛奶、糖浆、奶泡)动态组合
-
核心角色
角色 英文名 职责 本示例对应 组件 Component 定义原始对象的接口 Coffee 具体组件 Concrete Component 需要被装饰的原始对象 Espresso、Americano 装饰器 Decorator 持有组件引用,实现组件接口 CoffeeDecorator 具体装饰器 Concrete Decorator 添加具体职责 MilkDecorator、SugarDecorator -
优缺点分析
✅ 优点
优点 说明 动态扩展 运行时添加或撤销职责,比继承更灵活 避免类爆炸 替代静态继承,M个装饰器组合出2^M种行为 单一职责 每个装饰器只负责一个功能 开闭原则 新增装饰器无需修改原有代码 多层组合 可以嵌套多层装饰,实现复杂行为 ❌ 缺点
缺点 说明 解决方案 增加复杂度 多层装饰导致调试困难 控制装饰器数量 类型识别困难 装饰后对象类型与原对象不同 避免依赖具体类型 顺序依赖 装饰顺序可能影响结果 明确装饰器顺序约定 -
应用场景
场景类型 具体描述 真实案例 动态添加功能 需要在运行时给对象添加职责 咖啡订单、披萨配料 避免继承爆炸 大量组合导致子类过多 数据流包装(压缩、加密) 透明扩展 希望扩展对客户端透明 UI组件边框、滚动条 撤销功能 需要能够撤销添加的职责 游戏角色增益效果 -
应用示例:咖啡订单系统
typescript// ==================== 角色1:组件接口 ==================== // 咖啡接口 interface Coffee { getCost(): number; getDescription(): string; } // ==================== 角色2:具体组件 ==================== // 浓缩咖啡 class Espresso implements Coffee { getCost(): number { return 25; } getDescription(): string { return "浓缩咖啡"; } } // 美式咖啡 class Americano implements Coffee { getCost(): number { return 20; } getDescription(): string { return "美式咖啡"; } } // ==================== 角色3:装饰器抽象 ==================== // 咖啡装饰器(实现Coffee接口,持有Coffee引用) abstract class CoffeeDecorator implements Coffee { protected coffee: Coffee; constructor(coffee: Coffee) { this.coffee = coffee; } getCost(): number { return this.coffee.getCost(); } getDescription(): string { return this.coffee.getDescription(); } } // ==================== 角色4:具体装饰器 ==================== // 牛奶装饰器 class MilkDecorator extends CoffeeDecorator { getCost(): number { return this.coffee.getCost() + 5; } getDescription(): string { return this.coffee.getDescription() + " + 牛奶"; } } // 糖浆装饰器 class SugarDecorator extends CoffeeDecorator { getCost(): number { return this.coffee.getCost() + 3; } getDescription(): string { return this.coffee.getDescription() + " + 糖浆"; } } // 奶泡装饰器 class FoamDecorator extends CoffeeDecorator { getCost(): number { return this.coffee.getCost() + 4; } getDescription(): string { return this.coffee.getDescription() + " + 奶泡"; } } // 巧克力装饰器 class ChocolateDecorator extends CoffeeDecorator { getCost(): number { return this.coffee.getCost() + 6; } getDescription(): string { return this.coffee.getDescription() + " + 巧克力"; } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("装饰模式演示 - 咖啡订单系统"); console.log("=".repeat(60)); // 基础咖啡 console.log("\n【基础咖啡】"); let coffee: Coffee = new Espresso(); console.log(` ${coffee.getDescription()}: ¥${coffee.getCost()}`); // 单层装饰 console.log("\n【单层装饰】"); coffee = new MilkDecorator(coffee); console.log(` ${coffee.getDescription()}: ¥${coffee.getCost()}`); // 多层装饰(动态组合) console.log("\n【多层装饰 - 组合1】"); let coffee2: Coffee = new Americano(); coffee2 = new MilkDecorator(coffee2); coffee2 = new SugarDecorator(coffee2); coffee2 = new FoamDecorator(coffee2); console.log(` ${coffee2.getDescription()}: ¥${coffee2.getCost()}`); console.log("\n【多层装饰 - 组合2】"); let coffee3: Coffee = new Espresso(); coffee3 = new ChocolateDecorator(coffee3); coffee3 = new MilkDecorator(coffee3); console.log(` ${coffee3.getDescription()}: ¥${coffee3.getCost()}`); // 运行时动态添加/移除(通过重新赋值) console.log("\n【运行时动态调整】"); let myCoffee: Coffee = new Espresso(); console.log(` 基础: ${myCoffee.getDescription()} ¥${myCoffee.getCost()}`); myCoffee = new MilkDecorator(myCoffee); console.log(` 加牛奶: ${myCoffee.getDescription()} ¥${myCoffee.getCost()}`); myCoffee = new SugarDecorator(myCoffee); console.log(` 再加糖: ${myCoffee.getDescription()} ¥${myCoffee.getCost()}`); // 展示灵活性 console.log("\n【装饰器组合对比】"); const combinations = [ { name: "经典拿铁", build: () => new MilkDecorator(new Espresso()) }, { name: "摩卡", build: () => new ChocolateDecorator(new MilkDecorator(new Espresso())) }, { name: "焦糖玛奇朵", build: () => new FoamDecorator(new SugarDecorator(new MilkDecorator(new Espresso()))) } ]; combinations.forEach(combo => { const drink = combo.build(); console.log(` ${combo.name}: ${drink.getDescription()} ¥${drink.getCost()}`); }); console.log("\n✅ 装饰模式核心:动态添加职责,比继承更灵活"); } main();
外观
外观模式(Facade Pattern)为子系统中的一组接口提供一个统一的高层接口,使得子系统更容易使用。外观模式定义了一个简化接口,隐藏了子系统的复杂性。
-
通俗理解
遥控器:按一个按钮,电视自动完成信号切换、音量调节、画面调整
-
核心角色
角色 英文名 职责 本示例对应 外观 Facade 提供统一简化接口,委派请求给子系统 HomeTheaterFacade 子系统 Subsystems 实现具体功能,外观模式不改变它们 DVDPlayer、Projector、SoundSystem等 外观模式的结构
text客户端 → 外观(Facade) → 子系统A → 子系统B → 子系统C -
优缺点分析
✅ 优点
优点 说明 简化使用 客户端只需与外观交互,无需了解复杂子系统 降低耦合 客户端与子系统解耦,子系统变化不影响客户端 提高可读性 将复杂调用封装成有意义的接口 分层隔离 外观可以作为系统的分层边界 灵活切换 可以针对不同场景提供不同外观 ❌ 缺点
缺点 说明 解决方案 过度封装 可能隐藏子系统的可用功能 保留子系统直接访问的途径 增加代码 引入新的类和接口 仅在复杂子系统时使用 可能成为上帝对象 外观类承担过多职责 拆分多个外观类 -
应用场景
场景类型 具体描述 真实案例 复杂子系统 子系统非常复杂,需要简化接口 编译器、视频编码器 分层系统 需要为每层定义一个入口点 MVC架构中的Controller 遗留系统 为老旧复杂系统提供简化接口 数据库访问封装 第三方库 简化第三方库的调用方式 支付SDK封装 重构优化 逐步重构大型系统时作为缓冲层 业务逻辑门面 -
应用示例:家庭影院系统
typescript// ==================== 子系统 ==================== // DVD播放器 class DVDPlayer { on(): void { console.log(" 📀 DVD播放器 开机"); } off(): void { console.log(" 📀 DVD播放器 关机"); } play(movie: string): void { console.log(` 📀 播放电影: ${movie}`); } stop(): void { console.log(" 📀 停止播放"); } eject(): void { console.log(" 📀 弹出光盘"); } } // 投影仪 class Projector { on(): void { console.log(" 🎥 投影仪 开机"); } off(): void { console.log(" 🎥 投影仪 关机"); } setInput(source: string): void { console.log(` 🎥 输入源切换为: ${source}`); } setWideScreen(): void { console.log(" 🎥 设置为宽屏模式"); } } // 音响系统 class SoundSystem { on(): void { console.log(" 🔊 音响系统 开机"); } off(): void { console.log(" 🔊 音响系统 关机"); } setVolume(level: number): void { console.log(` 🔊 音量设置为: ${level}`); } setSurroundSound(): void { console.log(" 🔊 开启环绕声"); } } // 灯光 class Lights { dim(level: number): void { console.log(` 💡 灯光调暗至 ${level}%`); } on(): void { console.log(" 💡 灯光全亮"); } } // 屏幕 class Screen { down(): void { console.log(" 📺 幕布降下"); } up(): void { console.log(" 📺 幕布升起"); } } // ==================== 外观 ==================== // 家庭影院外观(统一简化接口) class HomeTheaterFacade { private dvd: DVDPlayer; private projector: Projector; private sound: SoundSystem; private lights: Lights; private screen: Screen; constructor() { this.dvd = new DVDPlayer(); this.projector = new Projector(); this.sound = new SoundSystem(); this.lights = new Lights(); this.screen = new Screen(); } // 一键看电影 watchMovie(movie: string): void { console.log("\n🎬 准备观影模式..."); this.lights.dim(20); this.screen.down(); this.projector.on(); this.projector.setWideScreen(); this.projector.setInput("DVD"); this.sound.on(); this.sound.setVolume(40); this.sound.setSurroundSound(); this.dvd.on(); this.dvd.play(movie); console.log("🍿 电影开始,享受吧!"); } // 一键结束观影 endMovie(): void { console.log("\n🛑 结束观影..."); this.dvd.stop(); this.dvd.eject(); this.dvd.off(); this.sound.off(); this.projector.off(); this.screen.up(); this.lights.on(); console.log("🏠 系统已关闭,欢迎下次使用"); } // 单独听音乐(不需要投影仪和屏幕) listenMusic(): void { console.log("\n🎵 音乐模式..."); this.lights.dim(30); this.sound.on(); this.sound.setVolume(25); this.sound.setSurroundSound(); console.log("🎧 开始播放音乐"); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("外观模式演示 - 家庭影院系统"); console.log("=".repeat(60)); // 创建外观(客户端只需与外观交互) const homeTheater = new HomeTheaterFacade(); // 一键看电影(背后复杂操作被封装) homeTheater.watchMovie("阿凡达"); // 一键结束 homeTheater.endMovie(); // 听音乐模式 homeTheater.listenMusic(); console.log("\n✅ 外观模式核心:为复杂子系统提供简化接口"); } main();
享元
享元模式(Flyweight Pattern)通过共享技术有效地支持大量细粒度对象的复用,减少内存占用。享元模式将对象的状态分为内部状态(可共享)和外部状态(不可共享)。
-
通俗理解
文字处理软件:每个字符不需要单独存储字体格式,相同字符共享格式信息
-
核心角色
角色 英文名 职责 本示例对应 享元接口 Flyweight 声明享元对象的公共方法 Character 具体享元 Concrete Flyweight 实现享元接口,存储内部状态 CharacterImpl 享元工厂 Flyweight Factory 创建并管理享元对象,确保共享 CharacterFactory 客户端 Client 维护外部状态,调用享元对象 TextEditor 享元模式的结构
text客户端 → 享元工厂 → 享元池(缓存) ↓ 具体享元(共享) -
优缺点分析
✅ 优点
优点 说明 节省内存 大量相似对象共享同一实例 提高性能 减少对象创建和垃圾回收开销 对象复用 相同内部状态的对象只创建一次 ❌ 缺点
缺点 说明 解决方案 增加复杂度 需要区分内部/外部状态 只在必要时使用 额外开销 享元工厂管理带来开销 使用简单缓存 外部状态维护 客户端需要维护外部状态 使用上下文对象 -
应用场景
场景类型 具体描述 真实案例 大量相似对象 对象数量巨大且相似度高 字符渲染、粒子系统 内存敏感 内存是瓶颈,需要优化 移动端应用 状态可分离 对象的大部分状态可以外部化 棋类游戏、图形编辑器 缓存场景 需要缓存共享资源 连接池、线程池 -
应用示例:文字编辑器字符缓存
typescript// ==================== 角色1:享元接口 ==================== // 字符享元接口 interface Character { display(row: number, col: number): void; getSymbol(): string; } // ==================== 角色2:具体享元 ==================== // 字符实现(内部状态:字符内容、字体、大小、颜色) class CharacterImpl implements Character { private symbol: string; // 内部状态 private font: string; // 内部状态 private size: number; // 内部状态 private color: string; // 内部状态 constructor(symbol: string, font: string, size: number, color: string) { this.symbol = symbol; this.font = font; this.size = size; this.color = color; console.log(` 🔤 创建新字符对象: ${symbol} (${font}, ${size}px, ${color})`); } getSymbol(): string { return this.symbol; } // 外部状态:位置(行列)在调用时传入 display(row: number, col: number): void { console.log(` 在位置[${row},${col}]显示: ${this.symbol} [${this.font},${this.size}px,${this.color}]`); } } // ==================== 角色3:享元工厂 ==================== // 字符工厂(管理享元池) class CharacterFactory { private characters: Map<string, Character> = new Map(); // 获取字符(如果存在则复用) getCharacter(symbol: string, font: string, size: number, color: string): Character { const key = `${symbol}_${font}_${size}_${color}`; if (!this.characters.has(key)) { const character = new CharacterImpl(symbol, font, size, color); this.characters.set(key, character); } return this.characters.get(key)!; } // 统计创建的字符数量 getCharacterCount(): number { return this.characters.size; } } // ==================== 客户端 ==================== // 文本编辑器(维护外部状态) class TextEditor { private factory: CharacterFactory; private content: Array<{ char: Character; row: number; col: number }> = []; constructor(factory: CharacterFactory) { this.factory = factory; } // 添加字符(外部状态:位置) addCharacter(symbol: string, font: string, size: number, color: string, row: number, col: number): void { const character = this.factory.getCharacter(symbol, font, size, color); this.content.push({ char: character, row, col }); } // 渲染所有字符 render(): void { console.log("\n📄 文档内容:"); this.content.forEach(item => { item.char.display(item.row, item.col); }); } // 统计 getStats(): void { console.log(`\n📊 统计: 共${this.content.length}个字符,享元对象${this.factory.getCharacterCount()}个`); } } // ==================== 程序入口 ==================== function main() { console.log("=".repeat(60)); console.log("享元模式演示 - 文字编辑器"); console.log("=".repeat(60)); const factory = new CharacterFactory(); const editor = new TextEditor(factory); console.log("\n【创建文档】"); // 创建大量字符(相同样式会被复用) editor.addCharacter('H', "宋体", 12, "黑色", 1, 1); editor.addCharacter('e', "宋体", 12, "黑色", 1, 2); editor.addCharacter('l', "宋体", 12, "黑色", 1, 3); editor.addCharacter('l', "宋体", 12, "黑色", 1, 4); editor.addCharacter('o', "宋体", 12, "黑色", 1, 5); editor.addCharacter('世', "黑体", 16, "红色", 2, 1); editor.addCharacter('界', "黑体", 16, "红色", 2, 2); // 复用已创建的字符 editor.addCharacter('H', "宋体", 12, "黑色", 3, 1); // 复用 editor.addCharacter('i', "宋体", 12, "黑色", 3, 2); // 不同样式需要新创建 editor.addCharacter('H', "楷体", 14, "蓝色", 4, 1); // 新创建 editor.addCharacter('e', "楷体", 14, "蓝色", 4, 2); // 新创建 editor.render(); editor.getStats(); // 展示享元模式的优势 console.log("\n【内存对比】"); console.log(` 文档字符数: 11 个`); console.log(` 实际对象数: ${factory.getCharacterCount()} 个`); console.log(` 节省对象: ${11 - factory.getCharacterCount()} 个`); console.log("\n✅ 享元模式核心:通过共享减少对象数量,节省内存"); } main();
代理
代理模式(Proxy Pattern)为另一个对象提供一个替身或占位符以控制对这个对象的访问。代理对象与目标对象实现相同的接口,客户端通过代理间接访问目标对象,代理可以在访问前后添加额外的操作。
-
通俗理解
明星经纪人:粉丝联系明星需要通过经纪人,经纪人可以筛选请求
-
核心角色
角色 英文名 职责 本示例对应 抽象主题 Subject 定义真实对象和代理的共同接口 Image 真实主题 Real Subject 真正的业务对象 RealImage 代理 Proxy 控制对真实对象的访问 ImageProxy 代理模式的结构
text客户端 → 代理(Proxy) → 真实主题(RealSubject) ↓ 可添加控制逻辑 -
优缺点分析
✅ 优点
优点 说明 控制访问 可以在访问真实对象前后添加权限检查、日志等 延迟加载 仅在需要时才创建重量级对象 缓存结果 代理可以缓存耗时的操作结果 远程代理 隐藏对象位于不同地址空间的细节 保护代理 根据访问权限控制对象访问 ❌ 缺点
缺点 说明 解决方案 增加复杂度 引入新类,增加系统复杂度 只在必要时使用 响应延迟 增加间接层,可能影响响应速度 使用缓存优化 -
应用场景
场景类型 具体描述 真实案例 延迟加载 大对象创建成本高,需要时才创建 图片懒加载 访问控制 需要检查客户端是否有权限 权限验证代理 日志记录 记录方法调用信息 日志代理 缓存代理 缓存重复请求的结果 数据库查询缓存 远程代理 访问远程服务 RPC代理 -
应用示例:图片懒加载系统
typescript// ==================== 角色1:抽象主题 ==================== // 图片接口 interface Image { display(): void; getName(): string; } // ==================== 角色2:真实主题 ==================== // 真实图片(重量级对象,加载成本高) class RealImage implements Image { private name: string; private width: number; private height: number; constructor(name: string, width: number, height: number) { this.name = name; this.width = width; this.height = height; this.loadFromDisk(); } // 模拟从磁盘加载(耗时操作) private loadFromDisk(): void { console.log(` 📁 从磁盘加载图片: ${this.name} (${this.width}x${this.height})`); // 模拟加载耗时 for (let i = 0; i < 100000000; i++) { } // 模拟延迟 } display(): void { console.log(` 🖼️ 显示图片: ${this.name}`); } getName(): string { return this.name; } } // ==================== 角色3:代理 ==================== // 图片代理(延迟加载) class ImageProxy implements Image { private realImage: RealImage | null = null; private name: string; private width: number; private height: number; constructor(name: string, width: number, height: number) { this.name = name; this.width = width; this.height = height; console.log(` 🎭 创建图片代理: ${this.name}`); } // 延迟加载:只有需要显示时才真正加载图片 display(): void { if (this.realImage === null) { console.log(` ⏳ 首次访问,开始加载图片...`); this.realImage = new RealImage(this.name, this.width, this.height); } this.realImage.display(); } getName(): string { return this.name; } } // ==================== 客户端 ==================== // 图片画廊 class ImageGallery { private images: Image[] = []; addImage(image: Image): void { this.images.push(image); } showAll(): void { console.log("\n🖼️ 显示所有图片(触发加载):"); this.images.forEach(image => { image.display(); }); } showOnlyNames(): void { console.log("\n📋 图片列表(不加载):"); this.images.forEach(image => { console.log(` - ${image.getName()}`); }); } } // ==================== 程序入口 ==================== function main() { console.log("=".repeat(60)); console.log("代理模式演示 - 图片懒加载系统"); console.log("=".repeat(60)); const gallery = new ImageGallery(); // 创建多个图片代理(此时图片未真正加载) console.log("\n【创建图片代理】"); gallery.addImage(new ImageProxy("风景.jpg", 1920, 1080)); gallery.addImage(new ImageProxy("人物.png", 800, 1200)); gallery.addImage(new ImageProxy("动物.gif", 640, 480)); // 查看图片名称(不会触发加载) gallery.showOnlyNames(); // 显示图片(此时才真正加载) gallery.showAll(); // 再次显示图片(已加载,直接显示) console.log("\n🔄 再次显示所有图片(使用缓存):"); gallery.showAll(); console.log("\n✅ 代理模式核心:通过代理控制真实对象的访问"); } main();
行为型模式
关注对象交互与职责分配:这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。
| 模式 | 核心作用 |
|---|---|
| 责任链 | 传递请求直至处理 |
| 命令 | 将请求封装为对象 |
| 解释器 | 定义语言文法规则 |
| 迭代器 | 顺序访问聚合元素 |
| 中介者 | 减少对象间直接交互 |
| 备忘录 | 捕获/恢复对象状态 |
| 观察者 | 对象间一对多通知 |
| 状态 | 状态变化改变行为 |
| 策略 | 算法族可替换 |
| 模板方法 | 定义算法骨架 |
| 访问者 | 分离操作与数据结构 |
职责链
职责链模式(Chain of Responsibility Pattern)允许多个对象都有机会处理请求,从而避免请求的发送者与接收者之间的耦合。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
-
通俗理解
公司请假审批:员工请假 → 组长(3天内)→ 经理(7天内)→ 总监(15天内)
-
核心角色
角色 英文名 职责 本示例对应 处理器接口 Handler 定义处理请求的接口 Approver 具体处理器 Concrete Handler 实现处理逻辑,决定是否传递 TeamLeader、Manager、Director 客户端 Client 发起请求,构建职责链 Main 职责链模式的结构
text客户端 → 处理器A → 处理器B → 处理器C → ... ↓ ↓ ↓ (处理) (处理) (处理) -
优缺点分析
✅ 优点
优点 说明 解耦 请求发送者无需知道哪个处理器处理 动态组合 可以在运行时改变链的结构 单一职责 每个处理器只关心自己的处理逻辑 开闭原则 新增处理器无需修改现有代码 ❌ 缺点
缺点 说明 解决方案 请求未被处理 可能没有处理器能处理请求 设置默认处理器或兜底处理 性能问题 链路过长影响性能 限制链长度或使用跳转 调试困难 请求在链中流转,难以追踪 添加日志记录 -
应用场景
场景类型 具体描述 真实案例 审批流程 根据金额/级别决定审批人 请假、报销、采购 过滤拦截 请求需要经过多层过滤 Web过滤器、拦截器 事件处理 UI事件冒泡传递 点击事件传递 日志处理 不同级别不同输出 Log4j日志链 -
应用示例:公司请假审批系统
typescript// ==================== 角色1:处理器接口 ==================== // 审批人接口 interface Approver { setNext(approver: Approver): Approver; handleRequest(days: number): void; } // ==================== 角色2:具体处理器 ==================== // 组长(审批3天内) class TeamLeader implements Approver { private next: Approver | null = null; setNext(approver: Approver): Approver { this.next = approver; return approver; } handleRequest(days: number): void { if (days <= 3) { console.log(` ✅ 组长审批通过:请假${days}天`); } else if (this.next) { console.log(` ➡️ 组长无法审批${days}天,转交上级`); this.next.handleRequest(days); } else { console.log(` ❌ 无人能审批${days}天`); } } } // 经理(审批7天内) class Manager implements Approver { private next: Approver | null = null; setNext(approver: Approver): Approver { this.next = approver; return approver; } handleRequest(days: number): void { if (days <= 7) { console.log(` ✅ 经理审批通过:请假${days}天`); } else if (this.next) { console.log(` ➡️ 经理无法审批${days}天,转交上级`); this.next.handleRequest(days); } else { console.log(` ❌ 无人能审批${days}天`); } } } // 总监(审批15天内) class Director implements Approver { private next: Approver | null = null; setNext(approver: Approver): Approver { this.next = approver; return approver; } handleRequest(days: number): void { if (days <= 15) { console.log(` ✅ 总监审批通过:请假${days}天`); } else if (this.next) { console.log(` ➡️ 总监无法审批${days}天,转交上级`); this.next.handleRequest(days); } else { console.log(` ❌ 无人能审批${days}天`); } } } // 总经理(审批30天内) class GeneralManager implements Approver { private next: Approver | null = null; setNext(approver: Approver): Approver { this.next = approver; return approver; } handleRequest(days: number): void { if (days <= 30) { console.log(` ✅ 总经理审批通过:请假${days}天`); } else { console.log(` ❌ 请假${days}天超过最大限制,需董事会审批`); } } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("职责链模式演示 - 请假审批系统"); console.log("=".repeat(60)); // 构建职责链 const teamLeader = new TeamLeader(); const manager = new Manager(); const director = new Director(); const generalManager = new GeneralManager(); teamLeader.setNext(manager).setNext(director).setNext(generalManager); // 测试不同天数的请假 console.log("\n【请假1天】"); teamLeader.handleRequest(1); console.log("\n【请假5天】"); teamLeader.handleRequest(5); console.log("\n【请假10天】"); teamLeader.handleRequest(10); console.log("\n【请假20天】"); teamLeader.handleRequest(20); console.log("\n【请假40天】"); teamLeader.handleRequest(40); console.log("\n✅ 职责链模式核心:请求沿链传递,直到被处理"); } main();
命令
命令模式(Command Pattern)将请求封装为一个对象,从而允许你使用不同的请求、队列或日志来参数化其他对象。命令模式还支持可撤销操作。
-
通俗理解
1.餐厅点餐:顾客(发送者)点餐 → 服务员(调用者)写单 → 厨师(执行者)做菜
2.遥控器:按下按钮(命令)→ 电视(接收者)执行操作
-
核心角色
角色 英文名 职责 本示例对应 命令接口 Command 声明执行操作的接口 Command 具体命令 Concrete Command 实现命令,绑定接收者 TurnOnCommand、TurnOffCommand 接收者 Receiver 真正执行业务逻辑 Light、TV 调用者 Invoker 调用命令对象 RemoteControl 命令模式的结构
text客户端 → 调用者(Invoker) → 命令(Command) → 接收者(Receiver) ↓ 具体命令1、具体命令2... -
优缺点分析
✅ 优点
优点 说明 解耦 发送者和执行者完全解耦 可撤销 可以轻松实现撤销/重做功能 队列支持 命令可以放入队列排队执行 宏命令 多个命令可以组合成宏命令 日志记录 可以记录命令用于恢复 ❌ 缺点
缺点 说明 解决方案 类数量增加 每个命令都需要一个类 使用lambda表达式简化 复杂度提升 增加多层间接调用 仅在需要时使用 -
应用场景
场景类型 具体描述 真实案例 需要撤销操作 编辑器、图像处理软件 Ctrl+Z 需要队列执行 任务队列、线程池 异步任务处理 需要日志恢复 数据库事务 操作日志回放 宏命令 一键执行多个操作 宏录制、批处理 GUI按钮 按钮绑定不同命令 菜单项、工具栏 -
应用示例:智能家居遥控器
typescript// ==================== 角色1:接收者 ==================== // 灯光 class Light { private isOn: boolean = false; turnOn(): void { this.isOn = true; console.log(" 💡 灯光已打开"); } turnOff(): void { this.isOn = false; console.log(" 💡 灯光已关闭"); } } // 电视 class TV { private volume: number = 10; turnOn(): void { console.log(" 📺 电视已打开"); } turnOff(): void { console.log(" 📺 电视已关闭"); } volumeUp(): void { this.volume = Math.min(100, this.volume + 10); console.log(` 📺 音量: ${this.volume}`); } volumeDown(): void { this.volume = Math.max(0, this.volume - 10); console.log(` 📺 音量: ${this.volume}`); } } // 音响 class Stereo { turnOn(): void { console.log(" 🔊 音响已打开"); } turnOff(): void { console.log(" 🔊 音响已关闭"); } } // ==================== 角色2:命令接口 ==================== interface Command { execute(): void; undo(): void; } // ==================== 角色3:具体命令 ==================== // 开灯命令 class TurnOnLightCommand implements Command { private light: Light; constructor(light: Light) { this.light = light; } execute(): void { this.light.turnOn(); } undo(): void { this.light.turnOff(); } } // 关灯命令 class TurnOffLightCommand implements Command { private light: Light; constructor(light: Light) { this.light = light; } execute(): void { this.light.turnOff(); } undo(): void { this.light.turnOn(); } } // 开电视命令 class TurnOnTVCommand implements Command { private tv: TV; constructor(tv: TV) { this.tv = tv; } execute(): void { this.tv.turnOn(); } undo(): void { this.tv.turnOff(); } } // 关电视命令 class TurnOffTVCommand implements Command { private tv: TV; constructor(tv: TV) { this.tv = tv; } execute(): void { this.tv.turnOff(); } undo(): void { this.tv.turnOn(); } } // 音响开命令 class TurnOnStereoCommand implements Command { private stereo: Stereo; constructor(stereo: Stereo) { this.stereo = stereo; } execute(): void { this.stereo.turnOn(); } undo(): void { this.stereo.turnOff(); } } // 宏命令(组合多个命令) class MacroCommand implements Command { private commands: Command[]; constructor(commands: Command[]) { this.commands = commands; } execute(): void { console.log(" 🎬 执行宏命令..."); this.commands.forEach(cmd => cmd.execute()); } undo(): void { console.log(" ↩️ 撤销宏命令..."); this.commands.reverse().forEach(cmd => cmd.undo()); this.commands.reverse(); // 恢复顺序 } } // ==================== 角色4:调用者 ==================== // 遥控器 class RemoteControl { private history: Command[] = []; private currentCommand: Command | null = null; setCommand(command: Command): void { this.currentCommand = command; } pressButton(): void { if (this.currentCommand) { this.currentCommand.execute(); this.history.push(this.currentCommand); } } pressUndo(): void { const lastCommand = this.history.pop(); if (lastCommand) { lastCommand.undo(); } else { console.log(" ⚠️ 没有可撤销的操作"); } } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("命令模式演示 - 智能家居遥控器"); console.log("=".repeat(60)); // 创建接收者 const light = new Light(); const tv = new TV(); const stereo = new Stereo(); // 创建命令 const lightOn = new TurnOnLightCommand(light); const lightOff = new TurnOffLightCommand(light); const tvOn = new TurnOnTVCommand(tv); const tvOff = new TurnOffTVCommand(tv); const stereoOn = new TurnOnStereoCommand(stereo); // 创建宏命令(看电影模式) const movieMode = new MacroCommand([tvOn, stereoOn, lightOff]); // 创建遥控器 const remote = new RemoteControl(); // 测试单个命令 console.log("\n【单个命令测试】"); remote.setCommand(lightOn); remote.pressButton(); remote.pressUndo(); // 测试宏命令 console.log("\n【宏命令测试 - 看电影模式】"); remote.setCommand(movieMode); remote.pressButton(); console.log("\n【撤销宏命令】"); remote.pressUndo(); // 测试命令历史 console.log("\n【命令历史测试】"); remote.setCommand(lightOn); remote.pressButton(); remote.setCommand(tvOn); remote.pressButton(); remote.setCommand(stereoOn); remote.pressButton(); console.log("\n【连续撤销】"); remote.pressUndo(); // 撤销音响 remote.pressUndo(); // 撤销电视 remote.pressUndo(); // 撤销灯光 console.log("\n✅ 命令模式核心:将请求封装为对象,支持撤销和组合"); } main();
解释器
解释器模式(Interpreter Pattern)定义了一种语言的文法表示,并提供一个解释器来解释该语言中的句子。该模式用于处理具有简单语法规则的语言,通过定义类来表示每条语法规则。
-
通俗理解
计算器:输入"2+3*4",解释器解析并计算结果
-
核心角色
角色 英文名 职责 本示例对应 抽象表达式 Abstract Expression 声明解释操作的接口 Expression 终结符表达式 Terminal Expression 实现文法中的终结符 NumberExpression 非终结符表达式 Nonterminal Expression 实现文法中的非终结符 AddExpression、SubtractExpression 上下文 Context 存储全局信息 Context 解释器模式的结构
text客户端 → 上下文(Context) → 抽象表达式 ↓ 终结符表达式 非终结符表达式 -
优缺点分析
✅ 优点
优点 说明 易于扩展 新增语法规则只需添加新类 语法分离 语法规则与执行逻辑分离 灵活性高 可以方便地修改或扩展文法 ❌ 缺点
缺点 说明 解决方案 类数量膨胀 每条语法规则一个类 仅用于简单语法 复杂语法难维护 复杂文法导致类爆炸 使用语法分析器生成器 效率问题 递归调用可能影响性能 使用缓存优化 -
应用场景
场景类型 具体描述 真实案例 简单表达式计算 数学表达式、布尔表达式 计算器、公式解析 配置文件解析 自定义配置文件格式 规则引擎配置 SQL解析器 解析查询语句 数据库查询解析 正则表达式 模式匹配解释器 文本搜索 脚本语言 简单脚本语言解释 游戏脚本、模板引擎 -
应用示例:数学表达式计算器
typescript// ==================== 角色1:上下文 ==================== // 上下文(存储变量值) class Context { private variables: Map<string, number> = new Map(); setVariable(name: string, value: number): void { this.variables.set(name, value); } getVariable(name: string): number { return this.variables.get(name) || 0; } } // ==================== 角色2:抽象表达式 ==================== // 表达式接口 interface Expression { interpret(context: Context): number; } // ==================== 角色3:终结符表达式 ==================== // 数字表达式 class NumberExpression implements Expression { private value: number; constructor(value: number) { this.value = value; } interpret(context: Context): number { return this.value; } } // 变量表达式 class VariableExpression implements Expression { private name: string; constructor(name: string) { this.name = name; } interpret(context: Context): number { return context.getVariable(this.name); } } // ==================== 角色4:非终结符表达式 ==================== // 加法表达式 class AddExpression implements Expression { private left: Expression; private right: Expression; constructor(left: Expression, right: Expression) { this.left = left; this.right = right; } interpret(context: Context): number { return this.left.interpret(context) + this.right.interpret(context); } } // 减法表达式 class SubtractExpression implements Expression { private left: Expression; private right: Expression; constructor(left: Expression, right: Expression) { this.left = left; this.right = right; } interpret(context: Context): number { return this.left.interpret(context) - this.right.interpret(context); } } // 乘法表达式 class MultiplyExpression implements Expression { private left: Expression; private right: Expression; constructor(left: Expression, right: Expression) { this.left = left; this.right = right; } interpret(context: Context): number { return this.left.interpret(context) * this.right.interpret(context); } } // 除法表达式 class DivideExpression implements Expression { private left: Expression; private right: Expression; constructor(left: Expression, right: Expression) { this.left = left; this.right = right; } interpret(context: Context): number { return this.left.interpret(context) / this.right.interpret(context); } } // ==================== 解析器 ==================== // 简单表达式解析器(将字符串解析为表达式树) class ExpressionParser { static parse(tokens: string[]): Expression { // 简化解析:仅支持数字和运算符 if (tokens.length === 1) { return new NumberExpression(parseInt(tokens[0])); } // 查找运算符位置 for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if (token === '+' || token === '-' || token === '*' || token === '/') { const left = this.parse(tokens.slice(0, i)); const right = this.parse(tokens.slice(i + 1)); switch (token) { case '+': return new AddExpression(left, right); case '-': return new SubtractExpression(left, right); case '*': return new MultiplyExpression(left, right); case '/': return new DivideExpression(left, right); } } } return new NumberExpression(0); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("解释器模式演示 - 数学表达式计算器"); console.log("=".repeat(60)); const context = new Context(); // 设置变量 context.setVariable("x", 10); context.setVariable("y", 5); context.setVariable("z", 3); // 表达式1: 2 + 3 console.log("\n【表达式1: 2 + 3】"); const expr1 = new AddExpression( new NumberExpression(2), new NumberExpression(3) ); console.log(` 结果: ${expr1.interpret(context)}`); // 表达式2: (10 - 5) * 3 console.log("\n【表达式2: (10 - 5) * 3】"); const expr2 = new MultiplyExpression( new SubtractExpression( new NumberExpression(10), new NumberExpression(5) ), new NumberExpression(3) ); console.log(` 结果: ${expr2.interpret(context)}`); // 表达式3: 使用变量 x + y * z console.log("\n【表达式3: x + y * z (x=10, y=5, z=3)】"); const expr3 = new AddExpression( new VariableExpression("x"), new MultiplyExpression( new VariableExpression("y"), new VariableExpression("z") ) ); console.log(` 结果: ${expr3.interpret(context)}`); // 表达式4: 复杂表达式 (x + y) * (z + 2) console.log("\n【表达式4: (x + y) * (z + 2)】"); const expr4 = new MultiplyExpression( new AddExpression( new VariableExpression("x"), new VariableExpression("y") ), new AddExpression( new VariableExpression("z"), new NumberExpression(2) ) ); console.log(` 结果: ${expr4.interpret(context)}`); // 表达式5: 字符串解析 console.log("\n【表达式5: 解析字符串 "3 + 4 * 2"】"); const tokens = ["3", "+", "4", "*", "2"]; const expr5 = ExpressionParser.parse(tokens); console.log(` 解析结果: ${expr5.interpret(context)}`); // 修改变量值 console.log("\n【动态修改变量】"); context.setVariable("x", 20); console.log(` 修改 x = 20 后,表达式3结果: ${expr3.interpret(context)}`); console.log("\n✅ 解释器模式核心:为简单文法定义解释器,解析并执行语句"); } main();
迭代器
迭代器模式(Iterator Pattern)提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。迭代器模式将遍历行为从聚合对象中分离出来,使其独立变化。
-
通俗理解
播放器列表:上一首/下一首遍历歌曲,不关心歌曲存储结构
-
核心角色
角色 英文名 职责 本示例对应 迭代器接口 Iterator 定义遍历操作接口 Iterator 具体迭代器 Concrete Iterator 实现遍历逻辑 PlaylistIterator 聚合接口 Aggregate 定义创建迭代器接口 Playlist 具体聚合 Concrete Aggregate 实现创建迭代器方法 MusicPlaylist 迭代器模式的结构
text客户端 → 聚合(Aggregate) → 创建 → 迭代器(Iterator) ↓ 遍历元素 -
优缺点分析
✅ 优点
优点 说明 分离职责 遍历逻辑与集合本身分离 统一接口 不同集合可使用相同遍历方式 多种遍历 可同时支持正序、倒序、过滤等 封装性好 不暴露集合内部结构 并发安全 可以创建只读迭代器 ❌ 缺点
缺点 说明 解决方案 增加类数量 每个集合需要对应迭代器类 使用语言内置迭代器 遍历开销 迭代器对象创建有开销 使用轻量级迭代器 修改影响 遍历时修改集合可能出问题 使用快照迭代器或 fail-fast -
应用场景
场景类型 具体描述 真实案例 集合遍历 需要统一方式遍历不同集合 数组、链表、树、图 多种遍历 需要提供多种遍历方式 正序、倒序、筛选 隐藏实现 不想暴露集合内部结构 API设计、模块封装 并行遍历 多个遍历同时进行 多线程处理 -
应用示例:音乐播放器歌单遍历
typescript// ==================== 角色1:迭代器接口 ==================== // 迭代器接口 interface Iterator<T> { hasNext(): boolean; next(): T; current(): T; } // ==================== 角色2:具体迭代器 ==================== // 正序迭代器 class PlaylistIterator implements Iterator<string> { private songs: string[]; private position: number = 0; constructor(songs: string[]) { this.songs = songs; } hasNext(): boolean { return this.position < this.songs.length; } next(): string { if (this.hasNext()) { return this.songs[this.position++]; } throw new Error("没有更多元素"); } current(): string { if (this.position < this.songs.length) { return this.songs[this.position]; } throw new Error("当前位置无效"); } } // 倒序迭代器 class ReversePlaylistIterator implements Iterator<string> { private songs: string[]; private position: number; constructor(songs: string[]) { this.songs = songs; this.position = songs.length - 1; } hasNext(): boolean { return this.position >= 0; } next(): string { if (this.hasNext()) { return this.songs[this.position--]; } throw new Error("没有更多元素"); } current(): string { if (this.position >= 0) { return this.songs[this.position]; } throw new Error("当前位置无效"); } } // ==================== 角色3:聚合接口 ==================== // 聚合接口(可被遍历的集合) interface Playlist<T> { createIterator(): Iterator<T>; createReverseIterator(): Iterator<T>; add(item: T): void; getSize(): number; } // ==================== 角色4:具体聚合 ==================== // 音乐歌单(内部使用数组存储) class MusicPlaylist implements Playlist<string> { private songs: string[] = []; add(song: string): void { this.songs.push(song); } getSize(): number { return this.songs.length; } // 创建正序迭代器 createIterator(): Iterator<string> { return new PlaylistIterator(this.songs); } // 创建倒序迭代器 createReverseIterator(): Iterator<string> { return new ReversePlaylistIterator(this.songs); } // 获取内部数组(供迭代器使用) getSongs(): string[] { return this.songs; } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("迭代器模式演示 - 音乐播放器"); console.log("=".repeat(60)); // 创建歌单 const playlist = new MusicPlaylist(); playlist.add("1. 稻香 - 周杰伦"); playlist.add("2. 海阔天空 - Beyond"); playlist.add("3. 演员 - 薛之谦"); playlist.add("4. 光年之外 - 邓紫棋"); playlist.add("5. 消愁 - 毛不易"); // 正序遍历 console.log("\n【正序遍历歌单】"); const iterator = playlist.createIterator(); let index = 1; while (iterator.hasNext()) { console.log(` ${index++}. ${iterator.next()}`); } // 倒序遍历 console.log("\n【倒序遍历歌单】"); const reverseIterator = playlist.createReverseIterator(); index = 1; while (reverseIterator.hasNext()) { console.log(` ${index++}. ${reverseIterator.next()}`); } // 遍历过程中不暴露内部结构 console.log("\n【外部无法直接访问内部数组】"); console.log(" ✅ 歌单内部使用数组存储,但客户端无需知道"); console.log(" ✅ 添加新歌曲不影响遍历方式"); // 同时进行多个遍历 console.log("\n【多迭代器并行遍历】"); const iter1 = playlist.createIterator(); const iter2 = playlist.createIterator(); console.log(" 迭代器1: " + iter1.next()); console.log(" 迭代器1: " + iter1.next()); console.log(" 迭代器2: " + iter2.next()); console.log(" 迭代器1: " + iter1.next()); console.log(" 迭代器2: " + iter2.next()); console.log("\n✅ 迭代器模式核心:将遍历逻辑从集合中分离"); } main();
中介者
中介者模式(Mediator Pattern)通过一个中介对象来封装一系列对象之间的交互,使各对象不需要显式地相互引用,从而降低耦合,并且可以独立地改变它们之间的交互。
-
通俗理解
聊天室:用户通过聊天室发送消息,而不是直接私聊所有人
-
核心角色
角色 英文名 职责 本示例对应 中介者接口 Mediator 定义对象通信接口 ChatRoomMediator 具体中介者 Concrete Mediator 协调各对象交互 ChatRoom 同事接口 Colleague 定义对象接口 User 具体同事 Concrete Colleague 与其他对象通信通过中介者 ChatUser -
优缺点分析
✅ 优点
优点 说明 降低耦合 对象间不再直接引用,减少依赖 集中控制 交互逻辑集中在中介者,便于管理 简化对象 对象只需关注自身行为,无需处理交互细节 易扩展 新增同事类无需修改其他同事 ❌ 缺点
缺点 说明 解决方案 中介者庞大 所有交互逻辑集中,可能变得复杂 拆分多个中介者 单点故障 中介者出问题影响整个系统 设计容错机制 -
应用场景
场景类型 具体描述 真实案例 多对象交互 对象间存在复杂的引用关系 聊天室、消息系统 协调控制 需要集中控制多个对象的行为 MVC控制器 解耦组件 组件间不应直接依赖 UI组件通信 事件驱动 发布-订阅模式 事件总线 -
应用示例:聊天室系统
typescript// ==================== 角色1:中介者接口 ==================== // 聊天室中介者接口 interface ChatRoomMediator { sendMessage(message: string, sender: User): void; addUser(user: User): void; removeUser(user: User): void; } // ==================== 角色2:同事接口 ==================== // 用户接口 interface User { getName(): string; send(message: string): void; receive(message: string, sender: string): void; } // ==================== 角色3:具体中介者 ==================== // 聊天室(具体中介者) class ChatRoom implements ChatRoomMediator { private users: User[] = []; addUser(user: User): void { this.users.push(user); this.broadcast(`${user.getName()} 加入聊天室`, user); } removeUser(user: User): void { const index = this.users.indexOf(user); if (index !== -1) { this.users.splice(index, 1); this.broadcast(`${user.getName()} 离开聊天室`, user); } } sendMessage(message: string, sender: User): void { this.broadcast(message, sender); } private broadcast(message: string, sender: User): void { for (const user of this.users) { // 不给自己发送 if (user !== sender) { user.receive(message, sender.getName()); } } } } // ==================== 角色4:具体同事 ==================== // 聊天用户(具体同事) class ChatUser implements User { private name: string; private mediator: ChatRoomMediator; constructor(name: string, mediator: ChatRoomMediator) { this.name = name; this.mediator = mediator; } getName(): string { return this.name; } send(message: string): void { console.log(`\n[${this.name} 发送]: ${message}`); this.mediator.sendMessage(message, this); } receive(message: string, sender: string): void { console.log(`[${this.name} 收到] ${sender}: ${message}`); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("中介者模式演示 - 聊天室系统"); console.log("=".repeat(60)); // 创建中介者(聊天室) const chatRoom = new ChatRoom(); // 创建用户(同事) const alice = new ChatUser("Alice", chatRoom); const bob = new ChatUser("Bob", chatRoom); const charlie = new ChatUser("Charlie", chatRoom); const diana = new ChatUser("Diana", chatRoom); // 用户加入聊天室 console.log("\n【用户加入聊天室】"); chatRoom.addUser(alice); chatRoom.addUser(bob); chatRoom.addUser(charlie); // 发送消息 console.log("\n【发送消息】"); alice.send("大家好!我是Alice"); bob.send("Hi Alice!欢迎"); charlie.send("晚上好"); // 新用户加入 console.log("\n【新用户加入】"); chatRoom.addUser(diana); diana.send("大家好,我是Diana"); // 用户离开 console.log("\n【用户离开】"); chatRoom.removeUser(bob); alice.send("Bob离开了"); // 私聊效果(通过中介者实现) console.log("\n【中介者优势】"); console.log(" ✅ 用户之间没有直接引用"); console.log(" ✅ 新增用户不影响现有用户代码"); console.log(" ✅ 所有消息集中在中介者处理"); console.log("\n✅ 中介者模式核心:将多对多交互转化为一对多"); } main();
备忘录
备忘录模式(Memento Pattern)允许在不破坏封装的前提下,捕获并外部化一个对象的内部状态,并在之后将该对象恢复到之前保存的状态。
-
通俗理解
Ctrl+Z撤销:编辑器记录操作历史,一键撤销
-
核心角色
角色 英文名 职责 本示例对应 发起人 Originator 创建备忘录,恢复状态 TextEditor 备忘录 Memento 存储发起人内部状态 EditorMemento 管理者 Caretaker 保存备忘录,不能修改 HistoryManager 备忘录模式的结构
text发起人(Originator) → 创建 → 备忘录(Memento) → 存储 → 管理者(Caretaker) ↑ ↓ └────────── 恢复 ←─────────────┘ -
优缺点分析
✅ 优点
优点 说明 状态恢复 可以方便地恢复到历史状态 封装性好 发起人状态不对外暴露 简化发起人 发起人不需要管理历史状态 撤销/重做 天然支持撤销和重做功能 ❌ 缺点
缺点 说明 解决方案 内存消耗 保存大量备忘录会消耗内存 限制历史记录数量 性能开销 频繁保存状态影响性能 只保存必要状态 管理复杂 需要管理备忘录生命周期 设置过期策略 -
应用场景
场景类型 具体描述 真实案例 撤销操作 需要回退到之前状态 编辑器Ctrl+Z、PS历史记录 游戏存档 保存进度,支持读档 RPG游戏存档点 事务回滚 数据库操作失败回滚 ACID事务 快照备份 保存对象某一时刻状态 虚拟机快照 草稿保存 保存未提交的编辑内容 邮件草稿、表单草稿 -
应用示例:文本编辑器撤销系统
typescript// ==================== 角色1:备忘录 ==================== // 编辑器备忘录(存储文本状态) class EditorMemento { private content: string; private timestamp: Date; constructor(content: string) { this.content = content; this.timestamp = new Date(); } getContent(): string { return this.content; } getTimestamp(): Date { return this.timestamp; } } // ==================== 角色2:发起人 ==================== // 文本编辑器(可以创建和恢复备忘录) class TextEditor { private content: string = ""; type(words: string): void { this.content += words; console.log(` ✍️ 输入: "${words}"`); } getContent(): string { return this.content; } // 创建备忘录(保存当前状态) save(): EditorMemento { console.log(` 💾 保存状态: "${this.content}"`); return new EditorMemento(this.content); } // 从备忘录恢复状态 restore(memento: EditorMemento): void { this.content = memento.getContent(); console.log(` ↩️ 恢复到: "${this.content}"`); } } // ==================== 角色3:管理者 ==================== // 历史记录管理器(管理备忘录) class HistoryManager { private history: EditorMemento[] = []; private currentIndex: number = -1; // 保存新状态 push(memento: EditorMemento): void { // 清除当前位置之后的历史(新分支) this.history = this.history.slice(0, this.currentIndex + 1); this.history.push(memento); this.currentIndex++; console.log(` 📚 历史记录: ${this.history.length} 条`); } // 撤销 undo(): EditorMemento | null { if (this.currentIndex > 0) { this.currentIndex--; console.log(` ⏪ 撤销操作`); return this.history[this.currentIndex]; } console.log(` ⚠️ 无法撤销,已到最开始`); return null; } // 重做 redo(): EditorMemento | null { if (this.currentIndex < this.history.length - 1) { this.currentIndex++; console.log(` ⏩ 重做操作`); return this.history[this.currentIndex]; } console.log(` ⚠️ 无法重做,已到最新`); return null; } // 显示历史 showHistory(): void { console.log("\n📋 历史记录:"); this.history.forEach((m, i) => { const marker = i === this.currentIndex ? "👉 " : " "; console.log(` ${marker}[${i}] ${m.getContent()} (${m.getTimestamp().toLocaleTimeString()})`); }); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("备忘录模式演示 - 文本编辑器撤销系统"); console.log("=".repeat(60)); const editor = new TextEditor(); const history = new HistoryManager(); // 编辑文本并保存状态 console.log("\n【编辑文本】"); editor.type("Hello"); history.push(editor.save()); editor.type(" World"); history.push(editor.save()); editor.type("!"); history.push(editor.save()); console.log(`\n📄 当前内容: "${editor.getContent()}"`); // 撤销操作 console.log("\n【撤销操作】"); const undo1 = history.undo(); if (undo1) editor.restore(undo1); console.log(`📄 当前内容: "${editor.getContent()}"`); const undo2 = history.undo(); if (undo2) editor.restore(undo2); console.log(`📄 当前内容: "${editor.getContent()}"`); // 重做操作 console.log("\n【重做操作】"); const redo1 = history.redo(); if (redo1) editor.restore(redo1); console.log(`📄 当前内容: "${editor.getContent()}"`); // 新编辑(产生新分支) console.log("\n【新编辑(产生历史分支)】"); editor.type("!!!"); history.push(editor.save()); console.log(`📄 当前内容: "${editor.getContent()}"`); // 显示历史记录 history.showHistory(); // 封装性验证 console.log("\n【封装性验证】"); console.log(" ✅ 备忘录内部状态不对外暴露"); console.log(" ✅ 只有发起人能创建和恢复备忘录"); console.log(" ✅ 管理者只负责存储,无法修改内容"); console.log("\n✅ 备忘录模式核心:捕获并恢复对象状态,不破坏封装"); } main();
观察者
观察者模式(Observer Pattern)定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生变化时,会自动通知所有观察者,使它们能够自动更新。
-
通俗理解
股票行情:股民订阅股票,股价变动时收到提醒
-
核心角色
角色 英文名 职责 本示例对应 主题接口 Subject 注册、移除、通知观察者 StockSubject 具体主题 Concrete Subject 维护观察者列表,状态变化时通知 Stock 观察者接口 Observer 定义更新接口 Investor 具体观察者 Concrete Observer 实现更新逻辑 IndividualInvestor、InstitutionalInvestor 观察者模式的结构
text主题(Subject) ──通知──→ 观察者1(Observer1) ↓ ↓ 维护观察者列表 观察者2(Observer2) 观察者3(Observer3) -
优缺点分析
✅ 优点
优点 说明 松耦合 主题和观察者之间依赖关系弱 可扩展 新增观察者无需修改主题 支持广播 一个变化自动通知所有观察者 动态订阅 可以在运行时添加/移除观察者 ❌ 缺点
缺点 说明 解决方案 通知开销 观察者过多影响性能 使用异步通知 循环依赖 观察者和主题相互依赖可能导致死循环 添加检测机制 顺序不确定 观察者通知顺序不可控 使用优先级队列 -
应用场景
场景类型 具体描述 真实案例 事件驱动 需要响应事件变化 GUI按钮点击、键盘事件 数据同步 一个变化需要同步多个组件 MVVM数据绑定 消息推送 一对多的消息分发 推送通知、广播 状态监控 监控对象状态变化 传感器数据监控 -
应用示例:股票行情通知系统
typescript// ==================== 角色1:观察者接口 ==================== // 投资者接口(观察者) interface Investor { update(stockName: string, price: number): void; getName(): string; } // ==================== 角色2:主题接口 ==================== // 股票主题接口(被观察者) interface StockSubject { attach(observer: Investor): void; detach(observer: Investor): void; notify(): void; } // ==================== 角色3:具体观察者 ==================== // 个人投资者 class IndividualInvestor implements Investor { private name: string; constructor(name: string) { this.name = name; } getName(): string { return this.name; } update(stockName: string, price: number): void { console.log(` 👤 ${this.name} 收到通知: ${stockName} 价格变为 ¥${price}`); } } // 机构投资者 class InstitutionalInvestor implements Investor { private name: string; private minPrice: number; constructor(name: string, minPrice: number = 0) { this.name = name; this.minPrice = minPrice; } getName(): string { return this.name; } update(stockName: string, price: number): void { if (price >= this.minPrice) { console.log(` 🏢 ${this.name} 收到通知: ${stockName} 价格变为 ¥${price}`); } else { console.log(` 🏢 ${this.name} 忽略通知: ${stockName} 价格 ¥${price} 低于关注价`); } } } // ==================== 角色4:具体主题 ==================== // 股票(具体主题) class Stock implements StockSubject { private name: string; private price: number; private observers: Investor[] = []; constructor(name: string, initialPrice: number) { this.name = name; this.price = initialPrice; } attach(observer: Investor): void { this.observers.push(observer); console.log(` 📌 ${observer.getName()} 订阅了 ${this.name}`); } detach(observer: Investor): void { const index = this.observers.indexOf(observer); if (index !== -1) { this.observers.splice(index, 1); console.log(` ❌ ${observer.getName()} 取消订阅 ${this.name}`); } } notify(): void { console.log(`\n 📢 ${this.name} 通知所有订阅者:`); for (const observer of this.observers) { observer.update(this.name, this.price); } } setPrice(price: number): void { if (this.price !== price) { console.log(`\n💰 ${this.name} 价格变动: ¥${this.price} → ¥${price}`); this.price = price; this.notify(); } } getPrice(): number { return this.price; } getName(): string { return this.name; } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("观察者模式演示 - 股票行情通知系统"); console.log("=".repeat(60)); // 创建股票(主题) const apple = new Stock("苹果公司", 150); const google = new Stock("谷歌公司", 2800); // 创建投资者(观察者) const alice = new IndividualInvestor("Alice"); const bob = new IndividualInvestor("Bob"); const fund = new InstitutionalInvestor("先锋基金", 2000); // 订阅股票 console.log("\n【订阅股票】"); apple.attach(alice); apple.attach(bob); google.attach(fund); google.attach(alice); // 股票价格变动 console.log("\n【苹果公司价格变动】"); apple.setPrice(155); apple.setPrice(160); console.log("\n【谷歌公司价格变动】"); google.setPrice(2750); google.setPrice(2100); // 取消订阅 console.log("\n【取消订阅】"); apple.detach(bob); console.log("\n【再次价格变动】"); apple.setPrice(165); // 动态添加观察者 console.log("\n【动态添加观察者】"); const charlie = new IndividualInvestor("Charlie"); apple.attach(charlie); apple.setPrice(170); // 模式优势说明 console.log("\n【观察者模式优势】"); console.log(" ✅ 股票和投资者完全解耦"); console.log(" ✅ 新增投资者无需修改股票代码"); console.log(" ✅ 投资者可以动态订阅/取消订阅"); console.log(" ✅ 支持一对多广播通知"); console.log("\n✅ 观察者模式核心:定义一对多依赖,自动通知更新"); } main();
状态
状态模式(State Pattern)允许一个对象在其内部状态改变时改变它的行为。对象看起来好像修改了它的类。状态模式将状态相关的行为封装到独立的状态类中,并将状态转换逻辑集中管理。
-
通俗理解
订单状态:待支付、已支付、已发货、已完成,每个状态的操作不同
-
核心角色
角色 英文名 职责 本示例对应 上下文 Context 维护当前状态,定义客户端接口 Order 状态接口 State 定义状态行为接口 OrderState 具体状态 Concrete State 实现特定状态下的行为 PendingState、PaidState、ShippedState、CompletedState 状态模式的结构
text上下文(Context) ──持有──→ 当前状态(State) ↓ ↓ 维护状态引用 具体状态1、2、3... 委托给当前状态 -
优缺点分析
✅ 优点
优点 说明 单一职责 每个状态的行为封装在独立类中 开闭原则 新增状态无需修改现有状态类 消除条件分支 用多态替代复杂的if-else或switch 状态转换明确 状态间的转换逻辑集中管理 代码可读性 状态逻辑清晰,易于理解 ❌ 缺点
缺点 说明 解决方案 类数量增加 每个状态一个类 状态不复杂时使用枚举 上下文耦合 状态类需要持有上下文引用 通过构造函数传递 状态转换分散 转换逻辑可能在多个状态中 统一在上下文中管理 -
应用场景
场景类型 具体描述 真实案例 状态机 对象行为随状态变化而变化 订单状态、工作流引擎 消除条件分支 大量if-else或switch-case 游戏角色状态、电梯控制 多状态转换 状态间有复杂转换规则 TCP连接状态 动态行为 运行时可以改变对象行为 文档审批流程 -
应用示例:订单状态管理系统
typescript// ==================== 角色1:状态接口 ==================== // 订单状态接口 interface OrderState { pay(order: Order): void; ship(order: Order): void; confirm(order: Order): void; cancel(order: Order): void; getStatus(): string; } // ==================== 角色2:上下文 ==================== // 订单类(上下文) class Order { private currentState: OrderState; private orderId: string; constructor(orderId: string) { this.orderId = orderId; this.currentState = new PendingState(); // 初始状态 console.log(`📦 订单 ${orderId} 创建,状态: ${this.getStatus()}`); } setState(state: OrderState): void { this.currentState = state; console.log(` ↪️ 订单 ${this.orderId} 状态变为: ${this.getStatus()}`); } getStatus(): string { return this.currentState.getStatus(); } getOrderId(): string { return this.orderId; } // 委托给当前状态 pay(): void { console.log(`\n💳 订单 ${this.orderId} 尝试支付...`); this.currentState.pay(this); } ship(): void { console.log(`\n🚚 订单 ${this.orderId} 尝试发货...`); this.currentState.ship(this); } confirm(): void { console.log(`\n✅ 订单 ${this.orderId} 尝试确认收货...`); this.currentState.confirm(this); } cancel(): void { console.log(`\n❌ 订单 ${this.orderId} 尝试取消...`); this.currentState.cancel(this); } } // ==================== 角色3:具体状态 ==================== // 待支付状态 class PendingState implements OrderState { getStatus(): string { return "待支付"; } pay(order: Order): void { console.log(" ✓ 支付成功"); order.setState(new PaidState()); } ship(order: Order): void { console.log(" ✗ 未支付,无法发货"); } confirm(order: Order): void { console.log(" ✗ 未支付,无法确认收货"); } cancel(order: Order): void { console.log(" ✓ 订单已取消"); order.setState(new CancelledState()); } } // 已支付状态 class PaidState implements OrderState { getStatus(): string { return "已支付"; } pay(order: Order): void { console.log(" ✗ 订单已支付,请勿重复支付"); } ship(order: Order): void { console.log(" ✓ 发货成功"); order.setState(new ShippedState()); } confirm(order: Order): void { console.log(" ✗ 未发货,无法确认收货"); } cancel(order: Order): void { console.log(" ✓ 退款中,订单已取消"); order.setState(new CancelledState()); } } // 已发货状态 class ShippedState implements OrderState { getStatus(): string { return "已发货"; } pay(order: Order): void { console.log(" ✗ 订单已支付且发货,无法重复支付"); } ship(order: Order): void { console.log(" ✗ 订单已发货,请勿重复发货"); } confirm(order: Order): void { console.log(" ✓ 确认收货成功"); order.setState(new CompletedState()); } cancel(order: Order): void { console.log(" ✗ 已发货,无法取消,请联系客服"); } } // 已完成状态 class CompletedState implements OrderState { getStatus(): string { return "已完成"; } pay(order: Order): void { console.log(" ✗ 订单已完成,无法支付"); } ship(order: Order): void { console.log(" ✗ 订单已完成,无法发货"); } confirm(order: Order): void { console.log(" ✗ 订单已完成,请勿重复确认"); } cancel(order: Order): void { console.log(" ✗ 订单已完成,无法取消"); } } // 已取消状态 class CancelledState implements OrderState { getStatus(): string { return "已取消"; } pay(order: Order): void { console.log(" ✗ 订单已取消,无法支付"); } ship(order: Order): void { console.log(" ✗ 订单已取消,无法发货"); } confirm(order: Order): void { console.log(" ✗ 订单已取消,无法确认"); } cancel(order: Order): void { console.log(" ✗ 订单已取消,请勿重复取消"); } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("状态模式演示 - 订单状态管理系统"); console.log("=".repeat(60)); // 创建订单 const order = new Order("ORD-001"); // 测试各种操作 console.log("\n【测试正常流程】"); order.pay(); // 待支付 → 已支付 order.ship(); // 已支付 → 已发货 order.confirm(); // 已发货 → 已完成 // 测试新订单 console.log("\n" + "=".repeat(60)); const order2 = new Order("ORD-002"); console.log("\n【测试取消流程】"); order2.cancel(); // 待支付 → 已取消 // 测试已取消状态下的操作 order2.pay(); order2.ship(); // 测试异常流程 console.log("\n" + "=".repeat(60)); const order3 = new Order("ORD-003"); console.log("\n【测试异常操作】"); order3.ship(); // 未支付无法发货 order3.confirm(); // 未支付无法确认 order3.pay(); // 正常支付 order3.pay(); // 重复支付(被拒绝) order3.ship(); // 正常发货 order3.ship(); // 重复发货(被拒绝) // 模式优势说明 console.log("\n" + "=".repeat(60)); console.log("\n【状态模式优势】"); console.log(" ✅ 消除了大量的 if-else 判断"); console.log(" ✅ 每个状态的行为独立封装"); console.log(" ✅ 新增状态不影响现有代码"); console.log(" ✅ 状态转换逻辑清晰明确"); console.log(" ✅ 符合开闭原则和单一职责原则"); console.log("\n✅ 状态模式核心:将状态相关的行为封装到独立的状态类中"); } main();
策略
策略模式(Strategy Pattern)定义了一系列算法,将每个算法封装起来,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端,实现了算法定义和使用分离。
-
通俗理解
支付方式:微信支付、支付宝、银行卡、现金,可以自由切换
-
核心角色
角色 英文名 职责 本示例对应 上下文 Context 持有策略引用,负责调用策略 PaymentContext 策略接口 Strategy 定义算法公共接口 PaymentStrategy 具体策略 Concrete Strategy 实现具体算法 WechatPay、Alipay、CreditCardPay 策略模式的结构
text上下文(Context) ──持有──→ 策略接口(Strategy) ↓ 具体策略1、具体策略2、具体策略3... -
优缺点分析
✅ 优点
优点 说明 开闭原则 新增策略无需修改上下文和其他策略 消除条件分支 用多态替代if-else或switch 算法复用 策略可以在不同上下文中复用 运行时切换 可以在运行时动态切换算法 单元测试友好 可以独立测试每个策略 ❌ 缺点
缺点 说明 解决方案 类数量增加 每个策略一个类 使用函数式策略(Lambda) 客户端需了解策略 客户端需要知道有哪些策略 结合工厂模式创建策略 策略类通信开销 策略间可能需要数据传递 使用上下文传递数据 -
应用场景
场景类型 具体描述 真实案例 多种算法变体 同一功能有多种实现方式 排序、压缩、加密 运行时切换 需要在运行时动态选择算法 支付方式、折扣计算 避免条件判断 大量if-else或switch-case 价格计算、路由选择 算法隔离 算法的实现细节对外隐藏 策略类只暴露接口 -
应用示例:支付系统多策略选择
typescript// ==================== 角色1:策略接口 ==================== // 支付策略接口 interface PaymentStrategy { pay(amount: number): void; getType(): string; } // ==================== 角色2:具体策略 ==================== // 微信支付 class WechatPay implements PaymentStrategy { private phoneNumber: string; constructor(phoneNumber: string) { this.phoneNumber = phoneNumber; } pay(amount: number): void { console.log(` 📱 微信支付: ${amount}元`); console.log(` 手机号: ${this.phoneNumber}`); console.log(` ✅ 支付成功`); } getType(): string { return "微信支付"; } } // 支付宝支付 class Alipay implements PaymentStrategy { private email: string; constructor(email: string) { this.email = email; } pay(amount: number): void { console.log(` 📱 支付宝支付: ${amount}元`); console.log(` 账号: ${this.email}`); console.log(` ✅ 支付成功`); } getType(): string { return "支付宝"; } } // 信用卡支付 class CreditCardPay implements PaymentStrategy { private cardNumber: string; private cvv: string; constructor(cardNumber: string, cvv: string) { this.cardNumber = cardNumber; this.cvv = cvv; } pay(amount: number): void { console.log(` 💳 信用卡支付: ${amount}元`); console.log(` 卡号: ****${this.cardNumber.slice(-4)}`); console.log(` ✅ 支付成功`); } getType(): string { return "信用卡"; } } // 现金支付 class CashPay implements PaymentStrategy { pay(amount: number): void { console.log(` 💵 现金支付: ${amount}元`); console.log(` ✅ 支付成功`); } getType(): string { return "现金"; } } // ==================== 角色3:上下文 ==================== // 支付上下文 class PaymentContext { private strategy: PaymentStrategy; constructor(strategy: PaymentStrategy) { this.strategy = strategy; console.log(`🏪 支付上下文创建,默认策略: ${this.strategy.getType()}`); } // 动态切换策略 setStrategy(strategy: PaymentStrategy): void { this.strategy = strategy; console.log(`🔄 切换支付策略为: ${this.strategy.getType()}`); } // 执行支付 executePayment(amount: number): void { console.log(`\n💰 支付金额: ¥${amount}`); console.log(`📌 使用策略: ${this.strategy.getType()}`); this.strategy.pay(amount); } // 批量支付(演示策略复用) batchPay(amounts: number[]): void { console.log(`\n📦 批量支付(共${amounts.length}笔)`); amounts.forEach((amount, index) => { console.log(`\n 第${index + 1}笔:`); this.strategy.pay(amount); }); } } // ==================== 策略工厂(可选扩展)==================== // 根据类型创建策略 class PaymentStrategyFactory { static createStrategy(type: string, config?: any): PaymentStrategy { switch (type) { case "wechat": return new WechatPay(config?.phone || "13800000000"); case "alipay": return new Alipay(config?.email || "user@example.com"); case "creditcard": return new CreditCardPay(config?.cardNumber || "1234567890123456", config?.cvv || "123"); case "cash": return new CashPay(); default: throw new Error(`不支持的支付类型: ${type}`); } } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("策略模式演示 - 支付系统"); console.log("=".repeat(60)); // 演示1:直接使用策略 console.log("\n【演示1:直接使用策略】"); const wechat = new WechatPay("13812345678"); const context = new PaymentContext(wechat); context.executePayment(100); // 演示2:动态切换策略 console.log("\n【演示2:运行时切换策略】"); context.setStrategy(new Alipay("user@example.com")); context.executePayment(200); context.setStrategy(new CreditCardPay("1234567890123456", "123")); context.executePayment(500); // 演示3:使用策略工厂 console.log("\n【演示3:策略工厂动态创建】"); const userChoice = "alipay"; // 假设从用户界面选择 const strategy = PaymentStrategyFactory.createStrategy(userChoice, { email: "zhangsan@example.com" }); const factoryContext = new PaymentContext(strategy); factoryContext.executePayment(300); // 演示4:批量支付(策略复用) console.log("\n【演示4:批量支付】"); const batchContext = new PaymentContext(new CashPay()); batchContext.batchPay([50, 80, 120]); // 演示5:不同策略对比 console.log("\n【演示5:策略对比】"); const amounts = [100, 200]; const strategies = [ new WechatPay("13800000000"), new Alipay("user@example.com"), new CreditCardPay("1234567890123456", "123") ]; strategies.forEach(strategy => { console.log(`\n使用 ${strategy.getType()}:`); const testContext = new PaymentContext(strategy); amounts.forEach(amount => { testContext.executePayment(amount); }); }); // 模式优势说明 console.log("\n" + "=".repeat(60)); console.log("\n【策略模式优势】"); console.log(" ✅ 消除大量 if-else 判断"); console.log(" ✅ 算法可以独立变化"); console.log(" ✅ 支持运行时动态切换"); console.log(" ✅ 符合开闭原则"); console.log(" ✅ 提高代码复用性"); console.log("\n✅ 策略模式核心:定义算法族,封装可互换"); } main();
模板方法
模板方法模式(Template Method Pattern)在父类中定义一个操作中的算法骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法结构的情况下,重新定义算法的某些特定步骤。
-
通俗理解
数据导出:读取数据→转换格式→生成文件→发送通知(流程固定,格式可变)
-
核心角色
角色 英文名 职责 本示例对应 抽象类 Abstract Class 定义模板方法,声明抽象步骤 DataProcessor 具体类 Concrete Class 实现抽象步骤,定制行为 CSVProcessor、JSONProcessor、XMLProcessor 模板方法模式的结构
text抽象类(AbstractClass) ├── 模板方法(Template Method) → 定义算法骨架 ├── 抽象操作(Abstract Operation) → 子类必须实现 ├── 具体操作(Concrete Operation) → 子类共享 └── 钩子操作(Hook Operation) → 子类可选实现 ↓ 具体类(ConcreteClass) → 实现抽象操作 -
优缺点分析
✅ 优点
优点 说明 代码复用 公共代码放在父类,子类只需实现差异部分 扩展性好 新增子类即可改变行为,符合开闭原则 控制反转 父类控制流程,子类提供实现 维护方便 算法修改只需改父类模板方法 统一规范 强制子类遵循相同的算法结构 ❌ 缺点
缺点 说明 解决方案 继承局限 子类只能通过继承扩展 结合策略模式使用 抽象方法增多 每个差异步骤都是抽象方法 提供默认实现 复杂度增加 增加类的层次结构 合理控制继承深度 -
应用场景
场景类型 具体描述 真实案例 流程固定 算法步骤固定,部分步骤可定制 数据导出、文件处理 代码复用 多个类有相同流程,细节不同 构建工具、测试框架 框架开发 框架定义骨架,用户实现细节 JUnit、Spring模板 批量处理 相同流程处理不同类型数据 ETL数据处理 -
应用示例:数据导出处理框架
typescript// ==================== 角色1:抽象类 ==================== // 数据导出处理器(模板方法模式) abstract class DataProcessor { // 模板方法:定义算法骨架(不可被子类重写) public process(data: any[]): void { console.log(`\n📋 开始处理 ${this.getProcessorName()} 数据...`); // 步骤1:验证数据(共同步骤) this.validateData(data); // 步骤2:处理数据(共同步骤) const processedData = this.processData(data); // 步骤3:格式化数据(抽象方法,子类实现) const formattedData = this.formatData(processedData); // 步骤4:输出数据(共同步骤) this.outputData(formattedData); // 步骤5:可选步骤(钩子方法,子类可选实现) if (this.needNotification()) { this.sendNotification(); } console.log(`✅ ${this.getProcessorName()} 数据处理完成`); } // 具体方法:所有子类共享(可被子类覆盖) protected validateData(data: any[]): void { console.log(` 🔍 验证数据: ${data.length} 条记录`); if (data.length === 0) { throw new Error("数据为空,无法处理"); } } // 具体方法:处理数据(可被子类覆盖) protected processData(data: any[]): any[] { console.log(` ⚙️ 处理数据: 去除空值`); return data.filter(item => item !== null && item !== undefined); } // 抽象方法:格式化数据(子类必须实现) protected abstract formatData(data: any[]): string; // 具体方法:输出数据(可被子类覆盖) protected outputData(formattedData: string): void { console.log(` 📤 输出数据:`); console.log(` ${formattedData.substring(0, 200)}${formattedData.length > 200 ? "..." : ""}`); } // 钩子方法:是否需要通知(子类可选覆盖) protected needNotification(): boolean { return false; // 默认不需要通知 } // 钩子方法:发送通知(子类可选实现) protected sendNotification(): void { console.log(` 📧 发送完成通知`); } // 抽象方法:获取处理器名称 protected abstract getProcessorName(): string; } // ==================== 角色2:具体类 ==================== // CSV格式处理器 class CSVProcessor extends DataProcessor { protected formatData(data: any[]): string { console.log(` 📄 格式化为 CSV 格式`); if (data.length === 0) return ""; // 获取所有键 const keys = Object.keys(data[0]); const header = keys.join(","); const rows = data.map(item => keys.map(key => item[key]).join(",")); return [header, ...rows].join("\n"); } protected getProcessorName(): string { return "CSV"; } // 覆盖钩子方法:CSV导出需要通知 protected needNotification(): boolean { return true; } protected sendNotification(): void { console.log(` 📧 发送CSV文件下载链接到管理员邮箱`); } } // JSON格式处理器 class JSONProcessor extends DataProcessor { protected formatData(data: any[]): string { console.log(` 📄 格式化为 JSON 格式`); return JSON.stringify(data, null, 2); } protected getProcessorName(): string { return "JSON"; } // 覆盖钩子方法:JSON导出不需要通知 protected needNotification(): boolean { return false; } } // XML格式处理器 class XMLProcessor extends DataProcessor { protected formatData(data: any[]): string { console.log(` 📄 格式化为 XML 格式`); let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n'; data.forEach(item => { xml += ' <record>\n'; for (const [key, value] of Object.entries(item)) { xml += ` <${key}>${value}</${key}>\n`; } xml += ' </record>\n'; }); xml += '</data>'; return xml; } protected getProcessorName(): string { return "XML"; } // 覆盖具体方法:XML需要特殊输出 protected outputData(formattedData: string): void { console.log(` 📤 XML数据已生成,大小: ${formattedData.length} 字节`); // 只显示前200字符 console.log(` ${formattedData.substring(0, 200)}...`); } // XML导出需要通知 protected needNotification(): boolean { return true; } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("模板方法模式演示 - 数据导出框架"); console.log("=".repeat(60)); // 准备测试数据 const testData = [ { id: 1, name: "张三", age: 25, city: "北京" }, { id: 2, name: "李四", age: 30, city: "上海" }, { id: 3, name: "王五", age: 28, city: "广州" } ]; // 使用不同处理器导出数据 console.log("\n【CSV格式导出】"); const csvProcessor = new CSVProcessor(); csvProcessor.process(testData); console.log("\n【JSON格式导出】"); const jsonProcessor = new JSONProcessor(); jsonProcessor.process(testData); console.log("\n【XML格式导出】"); const xmlProcessor = new XMLProcessor(); xmlProcessor.process(testData); // 演示空数据处理 console.log("\n【空数据处理(展示验证步骤)】"); const emptyProcessor = new CSVProcessor(); try { emptyProcessor.process([]); } catch (error: any) { console.log(` ❌ 错误: ${error.message}`); } // 演示钩子方法的灵活性 console.log("\n【钩子方法效果】"); console.log(" CSV处理器: needNotification() = true → 发送通知"); console.log(" JSON处理器: needNotification() = false → 不发送通知"); console.log(" XML处理器: needNotification() = true → 发送通知"); // 模式优势说明 console.log("\n" + "=".repeat(60)); console.log("\n【模板方法模式优势】"); console.log(" ✅ 复用共同代码(验证、处理、输出步骤)"); console.log(" ✅ 子类只需实现差异部分(格式化)"); console.log(" ✅ 钩子方法提供灵活扩展点"); console.log(" ✅ 符合开闭原则(新增格式无需修改现有代码)"); console.log(" ✅ 强制子类遵循统一流程"); console.log("\n✅ 模板方法模式核心:定义算法骨架,延迟步骤到子类"); } main();
访问者
访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下,定义作用于这些元素的新操作。访问者模式将数据结构与数据操作分离。
-
通俗理解
超市收银:商品(数据结构)不变,但不同的促销活动(操作)可以计算出不同价格
-
核心角色
角色 英文名 职责 本示例对应 访问者接口 Visitor 为每个元素声明访问方法 Visitor 具体访问者 Concrete Visitor 实现访问方法,定义具体操作 TaxVisitor、PriceVisitor 元素接口 Element 定义接受访问者的方法 Product 具体元素 Concrete Element 实现接受方法,调用访问者 Electronics、Food、Book 对象结构 Object Structure 存储元素集合,提供遍历接口 ShoppingCart 访问者模式的结构
text对象结构(ObjectStructure) ──包含──→ 元素(Element) ↓ ↓ 遍历元素 接受访问者 ↓ ↓ 访问者(Visitor) ←────────── 调用访问方法 -
优缺点分析
✅ 优点
优点 说明 开闭原则 新增操作只需添加新访问者,无需修改元素类 集中相关操作 相关操作可以集中在一个访问者中 积累信息 访问者可以在遍历时累积状态 分离关注点 数据结构与操作分离,各司其职 ❌ 缺点
缺点 说明 解决方案 增加新元素困难 新增元素需要修改所有访问者 评估需求稳定性 破坏封装 访问者需要了解元素内部细节 提供公共访问方法 循环依赖 访问者依赖元素,元素依赖访问者 使用接口隔离 复杂度高 角色多,结构复杂 仅在必要时使用 -
应用场景
场景类型 具体描述 真实案例 对象结构稳定 元素类型固定,操作频繁变化 编译器、文档处理 相关操作分散 多个不相关类需要执行相同操作 税务计算、报表生成 操作依赖具体类型 不同类型需要不同处理 促销活动、价格计算 需要累积状态 遍历时需要收集信息 统计、汇总 -
应用示例:购物车价格计算与税务系统
typescript// ==================== 角色1:访问者接口 ==================== // 访问者接口 interface Visitor { visitElectronics(product: Electronics): number; visitFood(product: Food): number; visitBook(product: Book): number; } // ==================== 角色2:元素接口 ==================== // 产品接口(元素) interface Product { accept(visitor: Visitor): number; getName(): string; getPrice(): number; } // ==================== 角色3:具体元素 ==================== // 电子产品 class Electronics implements Product { private name: string; private price: number; constructor(name: string, price: number) { this.name = name; this.price = price; } getName(): string { return this.name; } getPrice(): number { return this.price; } accept(visitor: Visitor): number { return visitor.visitElectronics(this); } } // 食品 class Food implements Product { private name: string; private price: number; constructor(name: string, price: number) { this.name = name; this.price = price; } getName(): string { return this.name; } getPrice(): number { return this.price; } accept(visitor: Visitor): number { return visitor.visitFood(this); } } // 书籍 class Book implements Product { private name: string; private price: number; constructor(name: string, price: number) { this.name = name; this.price = price; } getName(): string { return this.name; } getPrice(): number { return this.price; } accept(visitor: Visitor): number { return visitor.visitBook(this); } } // ==================== 角色4:具体访问者 ==================== // 税务计算器(访问者) class TaxVisitor implements Visitor { private totalTax: number = 0; visitElectronics(product: Electronics): number { const tax = product.getPrice() * 0.18; // 电子产品18%税率 this.totalTax += tax; console.log(` ${product.getName()} (电子产品): ¥${product.getPrice()} × 18% = ¥${tax.toFixed(2)}`); return tax; } visitFood(product: Food): number { const tax = product.getPrice() * 0.08; // 食品8%税率 this.totalTax += tax; console.log(` ${product.getName()} (食品): ¥${product.getPrice()} × 8% = ¥${tax.toFixed(2)}`); return tax; } visitBook(product: Book): number { const tax = product.getPrice() * 0.05; // 书籍5%税率 this.totalTax += tax; console.log(` ${product.getName()} (书籍): ¥${product.getPrice()} × 5% = ¥${tax.toFixed(2)}`); return tax; } getTotalTax(): number { return this.totalTax; } } // 价格计算器(访问者) class PriceVisitor implements Visitor { private totalPrice: number = 0; visitElectronics(product: Electronics): number { const price = product.getPrice(); this.totalPrice += price; console.log(` ${product.getName()}: ¥${price}`); return price; } visitFood(product: Food): number { const price = product.getPrice(); this.totalPrice += price; console.log(` ${product.getName()}: ¥${price}`); return price; } visitBook(product: Book): number { const price = product.getPrice(); this.totalPrice += price; console.log(` ${product.getName()}: ¥${price}`); return price; } getTotalPrice(): number { return this.totalPrice; } } // 促销访问者(打折计算) class DiscountVisitor implements Visitor { private totalDiscount: number = 0; visitElectronics(product: Electronics): number { const discount = product.getPrice() * 0.1; // 电子产品9折 this.totalDiscount += discount; console.log(` ${product.getName()}: 折扣 ¥${discount.toFixed(2)} (10% off)`); return discount; } visitFood(product: Food): number { const discount = product.getPrice() * 0.05; // 食品95折 this.totalDiscount += discount; console.log(` ${product.getName()}: 折扣 ¥${discount.toFixed(2)} (5% off)`); return discount; } visitBook(product: Book): number { const discount = 0; // 书籍不打折 console.log(` ${product.getName()}: 无折扣`); return discount; } getTotalDiscount(): number { return this.totalDiscount; } } // ==================== 角色5:对象结构 ==================== // 购物车(对象结构) class ShoppingCart { private products: Product[] = []; addProduct(product: Product): void { this.products.push(product); console.log(` 📦 添加商品: ${product.getName()} - ¥${product.getPrice()}`); } // 接受访问者(遍历所有产品) accept(visitor: Visitor): void { console.log(`\n 🛒 购物车商品列表:`); for (const product of this.products) { product.accept(visitor); } } getProductCount(): number { return this.products.length; } } // ==================== 客户端 ==================== function main() { console.log("=".repeat(60)); console.log("访问者模式演示 - 购物车系统"); console.log("=".repeat(60)); // 创建购物车 const cart = new ShoppingCart(); // 添加商品 console.log("\n【添加商品】"); cart.addProduct(new Electronics("iPhone 15", 5999)); cart.addProduct(new Electronics("MacBook Pro", 14999)); cart.addProduct(new Food("巧克力", 88)); cart.addProduct(new Food("牛奶", 25)); cart.addProduct(new Book("JavaScript高级程序设计", 128)); cart.addProduct(new Book("设计模式", 99)); // 访问者1:计算总价 console.log("\n【计算总价】"); const priceVisitor = new PriceVisitor(); cart.accept(priceVisitor); console.log(`\n 💰 商品总价: ¥${priceVisitor.getTotalPrice()}`); // 访问者2:计算税费 console.log("\n【计算税费】"); const taxVisitor = new TaxVisitor(); cart.accept(taxVisitor); console.log(`\n 📋 总税费: ¥${taxVisitor.getTotalTax().toFixed(2)}`); // 访问者3:计算折扣 console.log("\n【计算折扣】"); const discountVisitor = new DiscountVisitor(); cart.accept(discountVisitor); console.log(`\n 🏷️ 总折扣: ¥${discountVisitor.getTotalDiscount().toFixed(2)}`); // 最终结算 console.log("\n【最终结算】"); const total = priceVisitor.getTotalPrice(); const discount = discountVisitor.getTotalDiscount(); const tax = taxVisitor.getTotalTax(); const finalPrice = total - discount + tax; console.log(` 商品总价: ¥${total}`); console.log(` - 折扣: ¥${discount.toFixed(2)}`); console.log(` + 税费: ¥${tax.toFixed(2)}`); console.log(` = 应付金额: ¥${finalPrice.toFixed(2)}`); // 演示:新增访问者很容易(符合开闭原则) console.log("\n【访问者模式优势】"); console.log(" ✅ 新增操作(如促销、统计)无需修改商品类"); console.log(" ✅ 相关操作(税务、折扣)集中在访问者中"); console.log(" ✅ 可以累积状态(统计总数)"); console.log(" ✅ 符合开闭原则(对扩展开放,对修改关闭)"); // 演示:商品类型固定,操作可扩展 console.log("\n【扩展性演示】"); console.log(" 📌 商品类型(电子、食品、书籍)保持稳定"); console.log(" 📌 新增功能只需添加新访问者:"); console.log(" - 报表访问者:生成购物报表"); console.log(" - 库存访问者:检查库存"); console.log(" - 推荐访问者:推荐相似商品"); console.log("\n✅ 访问者模式核心:将操作与数据结构分离,不修改类添加新功能"); } main();
总结
设计模式不是目标,而是工具。避免"模式驱动设计",始终从解决实际问题出发。