设计模式-桥接模式、组合模式

发布于:2025-06-29 ⋅ 阅读:(20) ⋅ 点赞:(0)

桥接模式和组合模式很相似,虽然都是组合,但是处理的场景不一样。

内容

桥接模式

组合模式

组合关系

桥接模式的组合是“拥有”关系

组合模式的组合是“包含”关系

关系方向

横向(抽象层 ↔ 实现层)

纵向(父节点 ↔ 子节点)

组合深度

通常一层(无嵌套)

支持多层递归嵌套

设计动机

避免继承耦合,灵活替换实现

统一操作部分与整体,简化复杂结构

典型应用

跨平台开发、驱动封装

文件系统、组织架构、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);
    }
}

设计原则:• 遵循单一职责• 迪米特法则(最少知识原则)• 开闭原则

使用场景:树形结构,如 文件夹/文件公司/部门。,可以考虑使用组合模式。

总结:

  1. 如果是为了灵活替换底层实现 → 用桥接模式。(如更换数据库驱动、渲染引擎)
  2. 如果是为了处理树形结构的统一操作 → 用组合模式。(如遍历目录、计算组织架构的总人数)

桥接模式和组合模式都用了组合(Composition),但就像「螺丝刀」和「菜刀」都用金属制造,却完全不是同一种工具——它们的用途和设计目标有本质区别。