设计模式-桥接模式、组合模式
桥接模式和组合模式很相似,虽然都是组合,但是处理的场景不一样。
内容 | 桥接模式 | 组合模式 |
组合关系 | 桥接模式的组合是“拥有”关系 | 组合模式的组合是“包含”关系 |
关系方向 | 横向(抽象层 ↔ 实现层) | 纵向(父节点 ↔ 子节点) |
组合深度 | 通常一层(无嵌套) | 支持多层递归嵌套 |
设计动机 | 避免继承耦合,灵活替换实现 | 统一操作部分与整体,简化复杂结构 |
典型应用 | 跨平台开发、驱动封装 | 文件系统、组织架构、UI组件树 |
桥接模式
Bridge(桥接)—对象结构型模式定义:将抽象和实现解耦,使得两者可以独立地变化。用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
AbsShape 封装了 IDrawProgram 接口,这样它的子类想从 “蓝色”切换到 “红色” 或者别的,只需 set 进去就行啦(你看,这 UML 图多像座桥)
需求:在原来圆形和方形进行涂颜色。
package com.example.bridge;public class BridgeDemo {
// 定义了抽象的绘制操作,具体的绘制方式(比如颜色)由实现类提供interface IDrawProgram {void drawShape(String shapeType);}// 实现了IDrawProgram接口,提供在红色中绘制的具体方式static class RedDrawProgram implements IDrawProgram {@Overridepublic void drawShape(String shapeType) {System.out.println("用红色绘制 " + shapeType + "。");}}// 实现了IDrawProgram接口,提供在蓝色中绘制的具体方式static class BlueDrawProgram implements IDrawProgram {@Overridepublic void drawShape(String shapeType) {System.out.println("用蓝色绘制 " + shapeType + "。");}}// 抽象形状 (Abstraction)
// 包含一个IDrawProgram的引用,负责将抽象和实现分离
// 形状的具体绘制行为委托给IDrawProgram的实现类static abstract class AbsShape {protected IDrawProgram mProgram; // 绘制程序的引用// 设置绘制程序的方法public void setProgram(IDrawProgram program) {this.mProgram = program;}// 抽象的绘制方法,由子类实现public abstract void draw();}// 圆形 (Refined Abstraction 1)
// 具体的形状,继承自AbsShape,并实现自己的绘制逻辑static class Circle extends AbsShape {@Overridepublic void draw() {if (mProgram != null) {// 将实际的绘制工作委托给mProgram对象mProgram.drawShape("圆形");} else {System.out.println("圆形没有设置绘制程序。");}}}// 方形 (Refined Abstraction 2)
// 具体的形状,继承自AbsShape,并实现自己的绘制逻辑static class Rectangle extends AbsShape {@Overridepublic void draw() {if (mProgram != null) {// 将实际的绘制工作委托给mProgram对象mProgram.drawShape("方形");} else {System.out.println("方形没有设置绘制程序。");}}}public static void main(String[] args) {// 创建具体的绘制程序实现IDrawProgram redProgram = new RedDrawProgram();IDrawProgram blueProgram = new BlueDrawProgram();// 创建具体的形状(细化抽象)Circle circle = new Circle();Rectangle rectangle = new Rectangle();// 为形状设置不同的绘制程序,展示桥接模式的灵活性System.out.println("--- 绘制圆形 ---");circle.setProgram(redProgram); // 将圆形与红色绘制程序桥接circle.draw(); // 输出: 用红色绘制 圆形。circle.setProgram(blueProgram); // 将圆形与蓝色绘制程序桥接circle.draw(); // 输出: 用蓝色绘制 圆形。System.out.println("\n--- 绘制方形 ---");rectangle.setProgram(blueProgram); // 将方形与蓝色绘制程序桥接rectangle.draw(); // 输出: 用蓝色绘制 方形。rectangle.setProgram(redProgram); // 将方形与红色绘制程序桥接rectangle.draw(); // 输出: 用红色绘制 方形。// 演示未设置绘制程序的情况Circle defaultCircle = new Circle();defaultCircle.draw(); // 输出: 圆形没有设置绘制程序。}
}
设计原则:• 遵循单一职责• 迪米特法则(最少知识原则)• 开闭原则
使用场景:1.在进行系统设计时,发现类的继承有N层时,可以考虑使用桥梁模式。
组合模式
组合模式(Composite Pattern)允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。
import java.util.ArrayList;
import java.util.List;// 1. Component(组件)接口:定义文件和文件夹的共同行为
interface FileSystemComponent {String getName();void display(int indent); // 用于演示层次结构
}// 2. Leaf(叶子节点):文件
class File implements FileSystemComponent {private String name;public File(String name) {this.name = name;}@Overridepublic String getName() {return name;}@Overridepublic void display(int indent) {// 根据缩进打印文件名称for (int i = 0; i < indent; i++) {System.out.print(" "); // 两个空格代表一个缩进}System.out.println("📄 " + name); // 使用表情符号更直观}
}// 3. Composite(组合节点):文件夹
class Folder implements FileSystemComponent {private String name;private List<FileSystemComponent> components; // 存储子组件(文件或文件夹)public Folder(String name) {this.name = name;this.components = new ArrayList<>();}@Overridepublic String getName() {return name;}// 添加子组件public void addComponent(FileSystemComponent component) {components.add(component);}// 移除子组件public void removeComponent(FileSystemComponent component) {components.remove(component);}@Overridepublic void display(int indent) {// 根据缩进打印文件夹名称for (int i = 0; i < indent; i++) {System.out.print(" ");}System.out.println("📁 " + name + "/"); // 文件夹通常以斜杠结尾// 递归地显示所有子组件for (FileSystemComponent component : components) {component.display(indent + 1); // 子组件的缩进增加}}
}// 客户端代码
public class CompositePatternDemo {public static void main(String[] args) {// 创建文件File file1 = new File("Document.txt");File file2 = new File("Image.jpg");File file3 = new File("Spreadsheet.xlsx");File file4 = new File("Report.pdf");// 创建文件夹Folder rootFolder = new Folder("Root");Folder documentsFolder = new Folder("Documents");Folder photosFolder = new Folder("Photos");Folder reportsFolder = new Folder("Reports");// 构建文件系统结构rootFolder.addComponent(documentsFolder);rootFolder.addComponent(photosFolder);rootFolder.addComponent(file4); // 直接在根目录下添加文件documentsFolder.addComponent(file1);documentsFolder.addComponent(file3);documentsFolder.addComponent(reportsFolder); // 文件夹中嵌套文件夹photosFolder.addComponent(file2);reportsFolder.addComponent(new File("Q1_Report.docx")); // 匿名文件System.out.println("--- 文件系统结构 ---");rootFolder.display(0); // 从根目录开始显示,初始缩进为0System.out.println("\n--- 移除一个文件后 ---");documentsFolder.removeComponent(file3); // 移除一个文件rootFolder.display(0);}
}
设计原则:• 遵循单一职责• 迪米特法则(最少知识原则)• 开闭原则
使用场景:树形结构,如 文件夹/文件
、公司/部门
。,可以考虑使用组合模式。
总结:
- 如果是为了灵活替换底层实现 → 用桥接模式。(如更换数据库驱动、渲染引擎)
- 如果是为了处理树形结构的统一操作 → 用组合模式。(如遍历目录、计算组织架构的总人数)
桥接模式和组合模式都用了组合(Composition),但就像「螺丝刀」和「菜刀」都用金属制造,却完全不是同一种工具——它们的用途和设计目标有本质区别。