系列定位软考软件设计师 / 系统架构设计师 — 结构型模式专题第 2 讲考察分值上午题 1-2 分下午题常作为代码填空或类图识别出现难度等级⭐⭐⭐⭐☆理解抽象与实现分离的思想是关键类图识别是高频考点一、考纲定位与模式定义1.1 考纲要求桥接模式在软考中属于结构型模式的中高难度内容。考察形式包括上午选择题判断场景描述所属模式识别桥接模式与适配器模式、装饰模式的区别判断类图中是否存在两个独立变化的维度抽象层与实现层下午设计题补全 Abstraction 类中持有的 Implementor 引用补全operation()方法中将调用委托给 Implementor 的逻辑识别类图中两个独立继承树的关系1.2 模式定义桥接模式将抽象部分与它的实现部分分离使它们都可以独立地变化。核心意图当一个类存在两个独立变化的维度如形状和绘制方式、消息类型和发送渠道如果采用多继承类的数量会呈指数级增长类爆炸。桥接模式通过组合代替继承将两个维度拆分为两个独立的继承树再通过一个 桥Abstraction 持有 Implementor 引用连接起来让两个维度可以独立扩展。通俗理解假设你要设计一个图形系统有圆形、矩形两种形状有红色、蓝色两种颜色。如果用多继承你需要RedCircle、BlueCircle、RedRectangle、BlueRectangle—— 共 4 个类。如果新增一个绿色需要再加 2 个类新增一个三角形需要再加 3 个类。这就是类爆炸。桥接模式的思路是把 形状 和 颜色 拆成两个独立的继承树。形状类里组合一个颜色对象。新增颜色只需扩展颜色树新增形状只需扩展形状树互不干扰。二、UML 类图与角色划分┌─────────────────────────────────────────────────────────┐ │ Client │ │ main() │ │ ├── Abstraction abs new RefinedAbstraction(impl); │ │ └── abs.operation(); │ └───────────────────────┬─────────────────────────────────┘ │ uses ▼ ┌─────────────────────────────────────────────────────────┐ │ Abstraction │ │ ── 抽象层 ── │ │ - impl: Implementor ◄── 桥接点组合实现层 │ │ operation() { │ │ impl.operationImpl(); // 委托给实现层 │ │ } │ └───────────────────────┬─────────────────────────────────┘ ┌────────────┴────────────┐ ▼ ▼ ┌─────────────────────┐ ┌─────────────────────┐ │ RefinedAbstractionA │ │ RefinedAbstractionB │ │ (如Circle) │ │ (如Rectangle) │ │ operation() │ │ operation() │ │ (可扩展抽象行为) │ │ (可扩展抽象行为) │ └─────────────────────┘ └─────────────────────┘ │ │ │ ┌─────────────────────┐ │ └──┤ interface ├──┘ │ Implementor │ │ operationImpl() │ └──────────┬──────────┘ ┌───────────┴───────────┐ ▼ ▼ ┌───────────────────┐ ┌──────────────────┐ │ ConcreteImplA │ │ ConcreteImplB │ │ (如RedColor) │ │ (如BlueColor) │ │ operationImpl() │ │ operationImpl()│ └───────────────────┘ └──────────────────┘角色职责软考填空关键词Abstraction抽象定义抽象类的接口组合Implementor 引用将操作委托给实现层abstract classprotected Implementor implRefinedAbstraction扩充抽象继承 Abstraction实现具体的抽象行为如具体的形状extends AbstractionImplementor实现接口定义实现类的接口与 Abstraction 的接口可以不同通常更简单interfaceoperationImpl()ConcreteImplementor具体实现实现 Implementor 接口完成具体的实现如具体的颜色implements ImplementorClient客户端创建 ConcreteImplementor 并注入 Abstraction调用抽象层接口new RefinedAbstraction(new ConcreteImpl())类图识别要点软考类图中桥接模式的核心特征是存在两个独立的继承树Abstraction 继承树 和 Implementor 继承树且 Abstraction 与 Implementor 之间是组合/聚合关系带空心菱形的实线。如果类图里只有一个继承树 一个转换类 → 适配器如果有两个独立继承树通过组合关联 → 桥接。三、场景一图形绘制系统GoF 经典例子形状与颜色分离业务背景设计一个图形系统支持圆形、矩形两种形状支持红色、蓝色两种颜色绘制。每个形状需要被绘制出来但绘制方式颜色与形状本身是两个独立变化的维度。使用桥接模式避免类爆炸。说明这是 GoF 原书中桥接模式的经典例子也是软考下午题最爱考的 两个独立维度 场景。如果没有桥接模式需要RedCircle、BlueCircle、RedRectangle、BlueRectangle共 4 个类如果用桥接只需要 2 个形状类 2 个颜色类 4 个类数量相同但扩展性完全不同。新增一个绿色桥接模式只需加 1 个类多继承则需要加 2 个类。3.1 代码实现// Implementor绘制接口实现层 public interface Color { void applyColor(); } // ConcreteImplementor红色实现 public class RedColor implements Color { Override public void applyColor() { System.out.println(涂上红色); } } // ConcreteImplementor蓝色实现 public class BlueColor implements Color { Override public void applyColor() { System.out.println(涂上蓝色); } } // Abstraction形状抽象类抽象层 public abstract class Shape { // 桥接点组合实现层接口 protected Color color; // 通过构造器注入实现层对象关键 public Shape(Color color) { this.color color; } // 抽象方法绘制形状由子类实现具体形状 public abstract void draw(); // 通用方法涂色委托给实现层 public void paint() { color.applyColor(); } } // RefinedAbstraction圆形扩充抽象 public class Circle extends Shape { public Circle(Color color) { super(color); } Override public void draw() { System.out.print(绘制圆形); paint(); } } // RefinedAbstraction矩形扩充抽象 public class Rectangle extends Shape { public Rectangle(Color color) { super(color); } Override public void draw() { System.out.print(绘制矩形); paint(); } } // Client客户端 public class Client { public static void main(String[] args) { Shape redCircle new Circle(new RedColor()); redCircle.draw(); Shape blueRectangle new Rectangle(new BlueColor()); blueRectangle.draw(); Shape blueCircle new Circle(new BlueColor()); blueCircle.draw(); } }关键体会Shape抽象层与Color实现层通过组合关联。新增一种颜色如GreenColor只需新增 1 个实现类所有形状自动支持绿色。新增一种形状如Triangle只需新增 1 个抽象类自动支持所有颜色。两个维度独立扩展互不干扰。四、场景二消息发送系统消息类型与发送渠道分离业务背景系统需要支持短信、邮件两种消息类型支持即时发送、延迟发送两种发送渠道。消息内容标题、正文的组装逻辑由消息类型决定发送逻辑由渠道决定。两个维度独立变化使用桥接模式解耦。说明这是软考下午题中常见的 业务逻辑 技术实现 分离场景。消息类型是业务抽象层发送渠道是技术实现层。4.1 代码实现// Implementor发送渠道接口实现层 public interface SendChannel { void doSend(String title, String content, String receiver); } // ConcreteImplementor即时发送 public class InstantChannel implements SendChannel { Override public void doSend(String title, String content, String receiver) { System.out.println([即时发送] 收件人: receiver , 标题: title); } } // ConcreteImplementor延迟发送定时队列 public class DelayedChannel implements SendChannel { Override public void doSend(String title, String content, String receiver) { System.out.println([延迟发送] 已加入定时队列收件人: receiver , 标题: title); } } // Abstraction消息抽象类抽象层 public abstract class Message { protected SendChannel channel; public Message(SendChannel channel) { this.channel channel; } public abstract String assembleTitle(); public abstract String assembleContent(); public void send(String receiver) { String title assembleTitle(); String content assembleContent(); channel.doSend(title, content, receiver); } } // RefinedAbstraction短信消息 public class SmsMessage extends Message { private String code; public SmsMessage(SendChannel channel, String code) { super(channel); this.code code; } Override public String assembleTitle() { return 验证码通知; } Override public String assembleContent() { return 您的验证码是: code 5分钟内有效。; } } // RefinedAbstraction邮件消息 public class EmailMessage extends Message { private String subject; private String body; public EmailMessage(SendChannel channel, String subject, String body) { super(channel); this.subject subject; this.body body; } Override public String assembleTitle() { return subject; } Override public String assembleContent() { return body; } } // Client public class Client { public static void main(String[] args) { Message sms new SmsMessage(new InstantChannel(), 384921); sms.send(13800138000); Message email new EmailMessage( new DelayedChannel(), 月度报表, 请查收附件中的月度销售报表。 ); email.send(zhangsancompany.com); } }关键体会消息类型Sms/Email和发送渠道Instant/Delayed是两个独立维度。新增一种消息类型如PushMessage只需扩展抽象层新增一种渠道如BatchChannel只需扩展实现层。客户端可以自由组合无需为每种组合创建新类。五、场景三跨平台 UI 渲染组件与渲染引擎分离业务背景设计一个跨平台 UI 框架需要支持按钮、文本框两种组件支持OpenGL、DirectX两种渲染引擎。组件决定 画什么位置、大小、文字渲染引擎决定 怎么画底层 API 调用。两个维度独立变化且未来可能新增更多组件和更多渲染引擎。说明这是实际开发中桥接模式的典型应用也是软考类图识别题的常见场景。抽象层是 UI 组件实现层是渲染引擎。5.1 代码实现// Implementor渲染引擎接口实现层 public interface RenderEngine { void renderButton(String text, int x, int y, int w, int h); void renderTextField(String text, int x, int y, int w, int h); } // ConcreteImplementorOpenGL 渲染 public class OpenGLEngine implements RenderEngine { Override public void renderButton(String text, int x, int y, int w, int h) { System.out.println([OpenGL] 绘制按钮 text 在 ( x , y ) 大小 w x h); } Override public void renderTextField(String text, int x, int y, int w, int h) { System.out.println([OpenGL] 绘制文本框 text 在 ( x , y ) 大小 w x h); } } // ConcreteImplementorDirectX 渲染 public class DirectXEngine implements RenderEngine { Override public void renderButton(String text, int x, int y, int w, int h) { System.out.println([DirectX] 绘制按钮 text 在 ( x , y ) 大小 w x h); } Override public void renderTextField(String text, int x, int y, int w, int h) { System.out.println([DirectX] 绘制文本框 text 在 ( x , y ) 大小 w x h); } } // AbstractionUI 组件抽象类抽象层 public abstract class UIComponent { protected RenderEngine engine; protected int x, y, width, height; public UIComponent(RenderEngine engine, int x, int y, int w, int h) { this.engine engine; this.x x; this.y y; this.width w; this.height h; } public abstract void draw(); } // RefinedAbstraction按钮组件 public class Button extends UIComponent { private String label; public Button(RenderEngine engine, int x, int y, int w, int h, String label) { super(engine, x, y, w, h); this.label label; } Override public void draw() { engine.renderButton(label, x, y, width, height); } } // RefinedAbstraction文本框组件 public class TextField extends UIComponent { private String text; public TextField(RenderEngine engine, int x, int y, int w, int h, String text) { super(engine, x, y, w, h); this.text text; } Override public void draw() { engine.renderTextField(text, x, y, width, height); } } // Client public class Client { public static void main(String[] args) { RenderEngine gl new OpenGLEngine(); UIComponent btn1 new Button(gl, 10, 10, 100, 30, 确定); UIComponent tf1 new TextField(gl, 10, 50, 200, 25, 请输入用户名); btn1.draw(); tf1.draw(); System.out.println(--- 切换渲染引擎 ---); RenderEngine dx new DirectXEngine(); UIComponent btn2 new Button(dx, 10, 10, 100, 30, 确定); UIComponent tf2 new TextField(dx, 10, 50, 200, 25, 请输入用户名); btn2.draw(); tf2.draw(); } }六、三种场景对比与演进思路维度场景一图形绘制场景二消息发送场景三跨平台 UI抽象层AbstractionShape形状Message消息UIComponent组件实现层ImplementorColor颜色SendChannel渠道RenderEngine引擎抽象层扩展Circle、RectangleSmsMessage、EmailMessageButton、TextField实现层扩展RedColor、BlueColorInstantChannel、DelayedChannelOpenGLEngine、DirectXEngine桥接点Shape组合ColorMessage组合SendChannelUIComponent组合RenderEngine委托逻辑color.applyColor()channel.doSend()engine.renderXxx()避免的问题形状×颜色的类爆炸消息类型×渠道的类爆炸组件×引擎的类爆炸软考考法下午大题补全抽象层委托逻辑下午大题补全构造器注入类图识别两个继承树七、桥接模式 vs 适配器模式核心对比对比项桥接模式适配器模式设计时机事前设计在系统设计阶段就将抽象与实现分离事后补救已有代码接口不兼容需要转换目的让抽象和实现独立变化避免类爆炸让不兼容的接口能够协同工作类图结构两个独立的继承树抽象层 实现层一个 Adapter 类连接 Target 和 Adaptee是否改变接口❌ 不改变抽象层和实现层各自定义自己的接口✅ 改变Adapter 将 Adaptee 接口转为 Target 接口关系类型组合/聚合Abstraction 持有 Implementor组合/继承Adapter 持有/继承 Adaptee类数量减少避免多继承的类爆炸增加新增 Adapter 类软考判断看到 两个独立维度、抽象与实现分离 → 桥接看到 接口转换、不兼容 → 适配器记忆口诀桥接是 事前拆桥抽象与实现分离适配器是 事后搭桥接口转换兼容。八、软考高频考点与易混淆辨析8.1 高频考点考点内容模式分类结构型模式GoF 23 正式成员核心四角色Abstraction抽象、RefinedAbstraction扩充抽象、Implementor实现接口、ConcreteImplementor具体实现核心思想组合代替继承将两个独立变化的维度拆分为两个继承树通过组合关联桥接点Abstraction 中声明protected Implementor impl字段通过构造器注入方法委托Abstraction 的operation()内部调用impl.operationImpl()解决的问题多继承导致的类爆炸M×N 个类 → MN 个类与适配器区别桥接事前设计抽象与实现分离适配器事后补救接口转换与装饰区别桥接两个独立维度分离装饰单一维度层层增强8.2 易混淆辨析桥接 vs 适配器 vs 装饰表格对比项桥接模式适配器模式装饰模式类图特征两个独立继承树 组合关联一个 Adapter 转换类递归组合结构Decorator 组合 Component维度数量两个独立维度一个转换维度一个增强维度设计目的抽象与实现分离独立扩展接口转换兼容旧代码动态增强功能是否新增功能❌ 不新增只是分离❌ 不新增只是转换✅ 新增功能Client 调用new Abstraction(new Implementor())new Adapter(new Adaptee())new Decorator(new Component())软考判断两个继承树 组合一个转换类 接口不同递归包装 接口相同九、真题风格模拟与代码填空模拟题 1上午选择题以下关于桥接模式的叙述中正确的是。A. 桥接模式属于创建型模式主要用于创建对象的抽象与实现分离B. 在桥接模式中Abstraction 通过继承 Implementor 来获得实现功能C. 桥接模式的核心思想是将抽象部分与实现部分分离使它们可以独立变化从而避免类爆炸D. 桥接模式与适配器模式没有本质区别都是用于接口转换的答案C解析A 错误桥接属于结构型模式不是创建型。B 错误Abstraction 通过组合/聚合持有引用Implementor而不是继承。如果用继承就退化为多继承失去了桥接的意义。C 正确这是桥接模式的核心定义和价值。D 错误两者有本质区别。桥接是事前设计抽象与实现分离适配器是事后补救接口转换兼容。模拟题 2下午代码填空 — 补全桥接点某系统使用桥接模式实现文件导出功能。抽象层为Exporter导出器实现层为ExportFormat导出格式。请补全1~4。// 实现层接口 interface ExportFormat { void writeFile(String content, String path); } // 具体实现层PDF 格式 class PdfFormat implements ExportFormat { public void writeFile(String content, String path) { System.out.println([PDF] 导出到: path); } } // 具体实现层Excel 格式 class ExcelFormat implements ExportFormat { public void writeFile(String content, String path) { System.out.println([Excel] 导出到: path); } } // 抽象层 abstract class Exporter { // 桥接点组合实现层 protected (1)______ format; public Exporter((2)______ format) { this.format format; } public abstract String prepareContent(); public void export(String path) { String content prepareContent(); (3)______.(4)______(content, path); } } // 扩充抽象层报表导出器 class ReportExporter extends Exporter { private String[][] data; public ReportExporter(ExportFormat format, String[][] data) { super(format); this.data data; } public String prepareContent() { StringBuilder sb new StringBuilder(报表数据\n); for (String[] row : data) sb.append(String.join(,, row)).append(\n); return sb.toString(); } } public class Client { public static void main(String[] args) { String[][] data {{Q1, 100万}, {Q2, 150万}}; Exporter exporter new ReportExporter(new PdfFormat(), data); exporter.export(/tmp/report.pdf); } }答案(1)ExportFormat(2)ExportFormat(3)format(4)writeFile阅卷要点(1) 必须是ExportFormat实现层接口类型体现面向接口编程。不能写PdfFormat或ExcelFormat等具体类。(2) 构造器参数类型必须是ExportFormat抽象因为客户端注入的可能是任何具体实现。(3) 必须是formatAbstraction 中持有的 Implementor 引用这是桥接模式的 桥。(4) 必须是writeFile这是 Implementor 接口定义的方法Abstraction 将操作委托给它。模拟题 3下午代码填空 — 类图识别与补全某系统使用桥接模式设计数据库访问层。抽象层为DatabaseAccessor访问器实现层为DatabaseDriver驱动。UserAccessor和OrderAccessor继承DatabaseAccessorMySQLDriver和OracleDriver实现DatabaseDriver。请补全1~3。// 实现层接口 interface DatabaseDriver { void connect(String url); void execute(String sql); void close(); } // 抽象层 abstract class DatabaseAccessor { protected (1)______ driver; public DatabaseAccessor((2)______ driver) { this.driver driver; } public abstract void query(String condition); protected void executeQuery(String sql) { driver.connect(jdbc:...); driver.execute(sql); driver.close(); } } // 扩充抽象层用户访问器 class UserAccessor extends DatabaseAccessor { public UserAccessor(DatabaseDriver driver) { super(driver); } public void query(String condition) { String sql SELECT * FROM users WHERE condition; (3)______; } } public class Client { public static void main(String[] args) { DatabaseAccessor accessor new UserAccessor(new MySQLDriver()); accessor.query(id 1001); } }答案(1)DatabaseDriver(2)DatabaseDriver(3)executeQuery(sql)阅卷要点(1)(2) 必须是DatabaseDriver实现层接口不能是具体驱动类。这是桥接模式的核心抽象层只依赖实现层接口不依赖具体实现。(3) 必须是executeQuery(sql)因为UserAccessor将具体查询逻辑委托给父类DatabaseAccessor的通用执行方法而父类再通过driver委托给具体驱动。体现了 抽象层定义流程实现层定义细节 的分工。十、常见陷阱与注意事项陷阱 1误认为桥接是创建型模式桥接属于结构型模式因为它解决的是类与类之间的结构关系抽象层与实现层的组合分离而不是对象的创建问题。软考上午题如果问以下属于创建型模式的是选项里出现桥接不能选。陷阱 2桥接点写成继承关系这是软考最高频的失分点。桥接模式的核心是组合代替继承❌ 错误class Shape extends RedColor多继承类爆炸✅ 正确class Shape { protected Color color; }组合独立扩展软考代码填空里Abstraction 与 Implementor 之间必须是组合/聚合关系protected Implementor xxx绝不能是继承。陷阱 3与适配器模式混淆这是软考最常考的模式辨析题。桥接两个独立继承树事前设计目的是分离维度、避免类爆炸。适配器一个转换类事后补救目的是接口转换、兼容旧代码。快速判断技巧看类图里有几个继承树。两个独立继承树 → 桥接一个继承树 一个转换类 → 适配器。陷阱 4Implementor 接口设计过细或过粗Implementor 的接口应该只包含实现层需要提供的原子操作如applyColor()、doSend()、writeFile()不应该包含业务逻辑。如果 Implementor 里出现了与 Abstraction 重复的业务方法说明分离不彻底违背了桥接模式的设计初衷。陷阱 5RefinedAbstraction 直接调用 Implementor虽然 RefinedAbstraction 可以访问父类的protected Implementor impl字段但规范的做法是RefinedAbstraction 只实现自己特有的抽象行为通用委托逻辑由 Abstraction 统一处理。如果 RefinedAbstraction 里直接写impl.xxx()虽然功能正确但代码结构不够优雅软考阅卷时通常不会扣分但最佳实践是通过父类方法间接委托。陷阱 6构造器注入 vs Setter 注入软考代码填空里Abstraction 注入 Implementor 通常通过构造器如public Shape(Color color)这样能保证对象创建时实现层就已确定。如果用 Setter 注入如setColor()虽然也算对但构造器注入更符合桥接模式 创建时绑定 的设计意图。十一、总结要点内容定义将抽象部分与它的实现部分分离使它们都可以独立地变化分类结构型模式GoF 23 正式成员核心四角色Abstraction抽象、RefinedAbstraction扩充抽象、Implementor实现接口、ConcreteImplementor具体实现核心思想组合代替继承两个独立维度拆分为两个继承树通过组合关联桥接点Abstraction 中声明protected Implementor impl通过构造器注入方法委托Abstraction 的operation()调用impl.operationImpl()解决的问题多继承导致的类爆炸M×N → MN与适配器区别桥接事前设计抽象与实现分离适配器事后补救接口转换与装饰区别桥接两个独立维度分离装饰单一维度层层增强软考重点类图识别两个独立继承树代码填空桥接点声明 委托调用与适配器的辨析答题技巧看到 两个独立维度、抽象与实现分离、避免类爆炸 → 桥接看到类图有两个继承树通过组合关联 → 确认桥接系列预告下一篇将讲组合模式咱们下回见。