桥接模式和组合模式很相似,虽然都是组合,但是处理的场景不一样。
内容 |
桥接模式 |
组合模式 |
组合关系 |
桥接模式的组合是“拥有”关系 |
组合模式的组合是“包含”关系 |
关系方向 |
横向(抽象层 ↔ 实现层) |
纵向(父节点 ↔ 子节点) |
组合深度 |
通常一层(无嵌套) |
支持多层递归嵌套 |
设计动机 |
避免继承耦合,灵活替换实现 |
统一操作部分与整体,简化复杂结构 |
典型应用 |
跨平台开发、驱动封装 |
文件系统、组织架构、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 {
@Override
public void drawShape(String shapeType) {
System.out.println("用红色绘制 " + shapeType + "。");
}
}
// 实现了IDrawProgram接口,提供在蓝色中绘制的具体方式
static class BlueDrawProgram implements IDrawProgram {
@Override
public 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 {
@Override
public void draw() {
if (mProgram != null) {
// 将实际的绘制工作委托给mProgram对象
mProgram.drawShape("圆形");
} else {
System.out.println("圆形没有设置绘制程序。");
}
}
}
// 方形 (Refined Abstraction 2)
// 具体的形状,继承自AbsShape,并实现自己的绘制逻辑
static class Rectangle extends AbsShape {
@Override
public 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;
}
@Override
public String getName() {
return name;
}
@Override
public 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<>();
}
@Override
public String getName() {
return name;
}
// 添加子组件
public void addComponent(FileSystemComponent component) {
components.add(component);
}
// 移除子组件
public void removeComponent(FileSystemComponent component) {
components.remove(component);
}
@Override
public 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); // 从根目录开始显示,初始缩进为0
System.out.println("\n--- 移除一个文件后 ---");
documentsFolder.removeComponent(file3); // 移除一个文件
rootFolder.display(0);
}
}
设计原则:• 遵循单一职责• 迪米特法则(最少知识原则)• 开闭原则
使用场景:树形结构,如 文件夹/文件
、公司/部门
。,可以考虑使用组合模式。
总结:
- 如果是为了灵活替换底层实现 → 用桥接模式。(如更换数据库驱动、渲染引擎)
- 如果是为了处理树形结构的统一操作 → 用组合模式。(如遍历目录、计算组织架构的总人数)
桥接模式和组合模式都用了组合(Composition),但就像「螺丝刀」和「菜刀」都用金属制造,却完全不是同一种工具——它们的用途和设计目标有本质区别。