结构型设计模式主要用于优化类与对象之间的组合关系,通过灵活组合、包装、拆分现有对象,解决接口不兼容、功能扩展、系统解耦、层级管理、内存优化等问题。这类模式不聚焦对象创建,而是侧重如何组织代码结构,让复杂系统变得简洁、灵活、易维护。
经典结构型模式共包含七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。下文结合 TypeScript 语言,从核心思想、适用场景、优缺点、完整代码、代码解析逐一讲解,所有代码严格遵循统一编码格式规范。
一、适配器模式
1. 核心思想
适配器模式也叫转换器模式,核心是创建一个中间适配类,将原有不兼容的接口进行转换,让原本无法协同工作的类正常交互。它不会修改原有业务代码,仅通过中间层做格式、方法名、参数的适配,分为对象适配器与类适配器,TS 开发中对象适配器最为常用。
2. 适用场景
- 对接老旧系统、历史接口、第三方 SDK,新旧接口规范不一致;
- 项目迭代升级,新业务接口与旧代码无法直接兼容;
- 需要复用现有类,但类提供的方法和当前系统要求的接口不匹配。
3. 优缺点
- 优点:隔离新旧代码、无需修改原有逻辑、提升代码复用性、解耦性强;
- 缺点:增加额外适配类,轻微提升代码复杂度;过多使用会让整体结构变得混乱。
4. 代码实现
// 旧系统接口(原有类,无法修改) class Old_Service { public Old_Receive_Data(): void { console.log("旧接口:接收传统格式数据"); } } // 新系统目标接口(当前业务统一规范) interface New_Target_Interface { Receive_Msg(): void; } // 适配器类:衔接新旧接口 class Service_Adapter implements New_Target_Interface { private old_service: Old_Service; constructor(service: Old_Service) { this.old_service = service; } public Receive_Msg(): void { // 调用旧接口方法,完成适配转换 this.old_service.Old_Receive_Data(); } } // 测试调用 let old_service = new Old_Service(); let adapter: New_Target_Interface = new Service_Adapter(old_service); adapter.Receive_Msg();5. 代码说明
- 代码缩进统一为 4 个空格,代码块
{紧跟行尾,}独占一行; - 赋值、关系运算符前后均添加空格,逗号后补充空格,符合格式要求;
- 外部仅依赖新接口,旧系统逻辑被完全隔离,后续替换旧服务也无需改动业务代码。
二、装饰器模式
1. 核心思想
在不修改原类代码、不使用继承的前提下,动态地为对象叠加额外功能。它采用层层包装的思路,支持多层功能组合,比静态继承更加灵活,是功能扩展的常用方案。
2. 适用场景
- 需要为对象动态、灵活地添加附加功能,且功能可随时撤销、组合;
- 类功能繁多,使用继承会导致子类数量爆炸;
- 统一主体功能,不同场景叠加不同附加能力(如日志、权限、校验、附加业务逻辑)。
3. 优缺点
- 优点:遵循开闭原则、功能组合灵活、多层扩展互不影响、拆分单一职责;
- 缺点:多层装饰后代码层级变深,排错与调试难度增加。
4. 代码实现
// 抽象主体接口 interface Drink { Get_Price(): number; Get_Name(): string; } // 基础主体类:原味饮品 class Base_Drink implements Drink { public Get_Price(): number { return 8; } public Get_Name(): string { return "基础饮品"; } } // 抽象装饰器父类 abstract class Drink_Decorator implements Drink { protected drink: Drink; constructor(drink: Drink) { this.drink = drink; } public abstract Get_Price(): number; public abstract Get_Name(): string; } // 具体装饰器:加珍珠 class Pearl_Decorator extends Drink_Decorator { public Get_Price(): number { return this.drink.Get_Price() + 2; } public Get_Name(): string { return this.drink.Get_Name() + " + 珍珠"; } } // 具体装饰器:加冰块 class Ice_Decorator extends Drink_Decorator { public Get_Price(): number { return this.drink.Get_Price() + 1; } public Get_Name(): string { return this.drink.Get_Name() + " + 冰块"; } } // 测试调用 let drink: Drink = new Base_Drink(); drink = new Pearl_Decorator(drink); drink = new Ice_Decorator(drink); console.log(`饮品:${drink.Get_Name()},总价:${drink.Get_Price()}`);5. 代码说明
- 所有方法、类命名采用首字母大写 + 下划线分隔;
- 装饰器嵌套包装主体对象,可自由调整装饰顺序与层数;
- 全程没有修改
Base_Drink原有代码,完全符合开闭原则。
三、代理模式
1. 核心思想
为目标对象提供一个代理对象,由代理控制对真实对象的访问。所有请求先经过代理,再转发给真实对象,可在请求前后追加通用逻辑,实现访问控制、功能增强。
常见分类:静态代理、动态代理、虚拟代理、远程代理等,前端 / Node 中静态代理使用最广泛。
2. 适用场景
- 权限控制:访问真实对象前做身份、权限校验;
- 日志埋点、请求统计、性能监控;
- 延迟加载:真实对象创建成本高,使用代理延迟实例化;
- 请求拦截、限流、缓存处理。
3. 优缺点
- 优点:统一管控访问入口、真实对象与附加逻辑解耦、职责清晰;
- 缺点:额外增加代理类,请求链路变长,轻微增加调用开销。
4. 代码实现
// 统一抽象主题接口 interface Request_Handler { Handle_Request(): void; } // 真实业务对象 class Real_Handler implements Request_Handler { public Handle_Request(): void { console.log("真实对象:执行业务核心逻辑"); } } // 代理对象 class Proxy_Handler implements Request_Handler { private real_handler: Real_Handler; constructor() { this.real_handler = new Real_Handler(); } public Handle_Request(): void { console.log("代理对象:前置权限校验"); this.real_handler.Handle_Request(); console.log("代理对象:后置日志记录"); } } // 测试调用 let proxy: Request_Handler = new Proxy_Handler(); proxy.Handle_Request();5. 代码说明
- 代理与真实对象实现同一接口,对外表现完全一致,调用方无感知;
- 前置、后置通用逻辑统一放在代理中,核心业务逻辑保留在真实类中,职责分离。
四、外观模式(门面模式)
1. 核心思想
提供一个统一的对外门面入口,封装内部多个复杂子系统,对外隐藏内部繁杂的调用逻辑。客户端只需要和门面类交互,无需关心多个子系统的调用顺序与依赖关系,简化整体使用难度。
2. 适用场景
- 系统由多个相互关联的子模块组成,外部调用流程复杂;
- 划分层级架构,隔离内外层代码,降低耦合;
- 重构老旧复杂系统,用门面统一收口对外接口。
3. 优缺点
- 优点:简化调用、降低耦合、统一入口、分层清晰;
- 缺点:门面类职责过重,若设计不合理会变成 “万能类”;新增子系统可能需要修改门面代码。
4. 代码实现
// 子系统 A class Sub_System_A { public Run_A(): void { console.log("子系统 A 执行初始化"); } } // 子系统 B class Sub_System_B { public Run_B(): void { console.log("子系统 B 执行业务处理"); } } // 子系统 C class Sub_System_C { public Run_C(): void { console.log("子系统 C 执行收尾清理"); } } // 外观门面类 class System_Facade { private sys_a: Sub_System_A; private sys_b: Sub_System_B; private sys_c: Sub_System_C; constructor() { this.sys_a = new Sub_System_A(); this.sys_b = new Sub_System_B(); this.sys_c = new Sub_System_C(); } // 统一对外方法,封装调用顺序 public Start_All_System(): void { this.sys_a.Run_A(); this.sys_b.Run_B(); this.sys_c.Run_C(); } } // 测试调用 let facade = new System_Facade(); facade.Start_All_System();5. 代码说明
- 客户端仅调用
System_Facade,完全不用感知内部三个子系统; - 子系统内部可独立维护,不影响外部调用逻辑。
五、桥接模式
1. 核心思想
将抽象层与实现层彻底分离,让两者可以独立扩展变化,避免多层继承导致的类爆炸问题。它使用组合替代继承,把多维度变化拆分为两个独立体系,自由组合使用。
2. 适用场景
- 系统存在两个或多个独立变化的维度,且维度之间需要自由组合;
- 不希望使用多层继承,控制类的数量;
- 需要跨平台、跨类型扩展,抽象与实现需要动态切换。
3. 优缺点
- 优点:拆分变化维度、扩展性极强、减少子类数量、解耦抽象与实现;
- 缺点:设计难度提升,需要提前梳理业务的变化维度。
4. 代码实现
// 实现层接口:颜色维度(独立变化维度) interface Color_Type { Show_Color(): void; } class Red_Color implements Color_Type { public Show_Color(): void { console.log("颜色:红色"); } } class Blue_Color implements Color_Type { public Show_Color(): void { console.log("颜色:蓝色"); } } // 抽象层父类:形状维度(另一独立变化维度) abstract class Shape { protected color: Color_Type; constructor(color: Color_Type) { this.color = color; } public abstract Draw_Shape(): void; } // 具体形状:圆形 class Circle_Shape extends Shape { constructor(color: Color_Type) { super(color); } public Draw_Shape(): void { process.stdout.write("绘制圆形,"); this.color.Show_Color(); } } // 具体形状:方形 class Square_Shape extends Shape { constructor(color: Color_Type) { super(color); } public Draw_Shape(): void { process.stdout.write("绘制方形,"); this.color.Show_Color(); } } // 测试调用 let red_circle = new Circle_Shape(new Red_Color()); red_circle.Draw_Shape(); let blue_square = new Square_Shape(new Blue_Color()); blue_square.Draw_Shape();5. 代码说明
- 形状、颜色两个维度完全独立,新增形状或新增颜色互不影响;
- 通过构造函数组合两个维度,替代传统多继承结构。
六、组合模式
1. 核心思想
将对象组织成树形层级结构,统一对待单个叶子对象和容器组合对象。调用方无需区分节点类型,使用一致的方式遍历、操作整棵树,非常适合层级化场景。
2. 适用场景
- 树形结构业务:系统菜单、文件目录、组织架构、权限树;
- 需要统一处理整体与部分,忽略节点差异的场景。
3. 优缺点
- 优点:统一操作接口、灵活构建树形结构、符合树形业务场景;
- 缺点:如果业务规则复杂,节点类型过多时,约束节点行为会比较困难。
4. 代码实现
// 抽象节点组件 abstract class Tree_Component { protected node_name: string; constructor(name: string) { this.node_name = name; } public abstract Show_Node_Info(): void; } // 叶子节点:最小单元,无子节点 class Leaf_Node extends Tree_Component { public Show_Node_Info(): void { console.log(`叶子节点:${this.node_name}`); } } // 容器节点:可包含子节点 class Container_Node extends Tree_Component { private child_list: Tree_Component[] = []; public Add_Child(node: Tree_Component): void { this.child_list.push(node); } public Show_Node_Info(): void { console.log(`容器节点:${this.node_name}`); for (let i = 0; i < this.child_list.length; i++) { this.child_list[i].Show_Node_Info(); } } } // 测试调用 let root = new Container_Node("根目录"); let folder = new Container_Node("文件夹"); let file_1 = new Leaf_Node("文件1"); let file_2 = new Leaf_Node("文件2"); folder.Add_Child(file_1); root.Add_Child(folder); root.Add_Child(file_2); root.Show_Node_Info();5. 代码说明
- 叶子节点与容器节点继承同一抽象类,对外接口完全一致;
- 天然适配文件目录、菜单等树形业务,层级扩展简单。
七、享元模式
1. 核心思想
复用大量重复对象,抽取对象中固定不变的内部状态作为共享数据,将频繁变化的外部状态单独传入,以此减少内存占用、降低对象创建数量。核心是对象复用、池化管理。
2. 适用场景
- 系统中存在大量相似对象,创建对象会消耗大量内存;
- 对象可拆分为「内部固定状态」和「外部可变状态」;
- 典型场景:游戏怪物、网页图标、弹窗样式、连接池等。
3. 优缺点
- 优点:极大减少内存开销、提升性能、复用对象资源;
- 缺点:需要拆分内外状态,增加代码逻辑;线程并发场景下需要注意对象状态安全。
4. 代码实现
const MAX_POOL_COUNT = 10; // 享元类:存储内部共享状态 class Flyweight_Object { private type_tag: string; constructor(tag: string) { this.type_tag = tag; } // 接收外部可变状态执行业务 public Use_Info(outside_state: string): void { console.log(`共享标识:${this.type_tag},外部状态:${outside_state}`); } } // 享元工厂:负责对象池管理、复用对象 class Flyweight_Factory { private object_pool: Map<string, Flyweight_Object> = new Map(); public Get_Object(tag: string): Flyweight_Object { if (!this.object_pool.has(tag)) { this.object_pool.set(tag, new Flyweight_Object(tag)); } return this.object_pool.get(tag)!; } } // 测试调用 let factory = new Flyweight_Factory(); let obj_1 = factory.Get_Object("icon_01"); let obj_2 = factory.Get_Object("icon_01"); obj_1.Use_Info("页面A"); obj_2.Use_Info("页面B"); if (obj_1 == obj_2) { console.log("两个引用指向同一个共享对象"); }5. 代码说明
- 常量
MAX_POOL_COUNT全大写 + 下划线命名,符合编码规范; - 工厂统一管理对象池,相同标识的对象只会创建一次,实现复用;
- 可变状态通过参数动态传入,不污染共享对象内部数据。
结构型模式综合总结
- 适配器:解决接口不兼容,做中间转换;
- 装饰器:动态给对象叠加功能,灵活扩展;
- 代理:控制对象访问,追加前置 / 后置通用逻辑;
- 外观:封装多子系统,提供统一简易调用入口;
- 桥接:拆分多维度变化,用组合替代继承,防类爆炸;
- 组合:构建树形结构,统一处理整体与局部节点;
- 享元:池化复用相似对象,大幅优化内存占用。
在 TypeScript / Node.js 项目开发中,可根据结构特点、扩展需求、性能要求选择对应模式,让代码结构更合理、架构更健壮。