结构型设计模式

67 阅读6分钟

结构型模式通过适配接口、桥接抽象与实现、组合树形结构、动态装饰、简化子系统外观、共享享元及代理控制访问,优化对象间协作,提升系统灵活性与复用性。


1. 适配器模式(Adapter)

定义

将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题。

类图

┌─────────────┐       ┌───────────────────┐
│ Target      │       │ Adaptee           │
├─────────────┤       ├───────────────────┤
│ +request()  │       │ +specificRequest()│
└──────┬──────┘       └──────────┬────────┘
       │                         │
       ▼                         ▼
┌───────────────────┐       ┌───────────────────┐
│ Adapter           │       │ Client            │
├───────────────────┤       ├───────────────────┤
│ -adaptee: Adaptee │       │                   │
│ +request()        │       │                   │
└───────────────────┘       └───────────────────┘

Java 实现

// 目标接口(客户端期望的接口)
interface MediaPlayer {
    void play(String audioType, String fileName);
}
​
// 被适配的类(现有功能,但接口不兼容)
class LegacyAudioPlayer {
    public void playMp3(String fileName) {
        System.out.println("Playing MP3: " + fileName);
    }
}
​
// 适配器类
class AudioAdapter implements MediaPlayer {
    private LegacyAudioPlayer legacyPlayer = new LegacyAudioPlayer();
​
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            legacyPlayer.playMp3(fileName);
        } else {
            System.out.println("Unsupported format: " + audioType);
        }
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        MediaPlayer player = new AudioAdapter();
        player.play("mp3", "song.mp3");  // 输出: Playing MP3: song.mp3
        player.play("avi", "movie.avi"); // 输出: Unsupported format: avi
    }
}

适用场景

  • 旧系统接口适配新系统
  • 第三方库接口与项目不兼容

优缺点

  • 优点:兼容性高,复用现有代码。
  • 缺点:过度使用会增加系统复杂度。

2. 桥接模式(Bridge)

定义

将抽象部分与实现部分分离,使它们可以独立变化。

类图

┌───────────────────┐       ┌───────────────────┐
│ Abstraction       │<>─────│ Implementor       │
├───────────────────┤       ├───────────────────┤
│ +operation()      │       │ +operationImpl()  │
└──────┬────────────┘       └──────────┬────────┘
       │                               │
       ▼                               ▼
┌───────────────────┐       ┌───────────────────┐
│ RefinedAbstraction│       │ ConcreteImplementor│
├───────────────────┤       ├───────────────────┤
│ +operation()      │       │ +operationImpl()  │
└───────────────────┘       └───────────────────┘

Java 实现

// 实现部分接口
interface Renderer {
    void renderShape(String shape);
}
​
// 具体实现:矢量渲染
class VectorRenderer implements Renderer {
    @Override
    public void renderShape(String shape) {
        System.out.println("Drawing " + shape + " as vector");
    }
}
​
// 具体实现:栅格渲染
class RasterRenderer implements Renderer {
    @Override
    public void renderShape(String shape) {
        System.out.println("Drawing " + shape + " as pixels");
    }
}
​
// 抽象部分
abstract class Shape {
    protected Renderer renderer;
    public Shape(Renderer renderer) {
        this.renderer = renderer;
    }
    public abstract void draw();
}
​
// 扩展抽象:圆形
class Circle extends Shape {
    public Circle(Renderer renderer) {
        super(renderer);
    }
    @Override
    public void draw() {
        renderer.renderShape("Circle");
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        Renderer vector = new VectorRenderer();
        Shape circle = new Circle(vector);
        circle.draw(); // 输出: Drawing Circle as vector
    }
}

适用场景

  • 多维度变化的系统(如形状 + 渲染方式)
  • 避免类爆炸(如 MxN 种组合)

优缺点

  • 优点:解耦抽象与实现,扩展性强。
  • 缺点:增加系统设计复杂度。

3. 组合模式(Composite)

定义

将对象组织成树形结构,统一处理单个对象和组合对象。

类图

┌───────────────────┐
│ Component         │
├───────────────────┤
│ +add()            │
│ +remove()         │
│ +getChild()       │
│ +operation()      │
└──────┬────────────┘
       △
       │
       ├──────────────────┐
       │                  │
┌───────────────┐  ┌───────────────┐
│ Leaf          │  │ Composite     │
├───────────────┤  ├───────────────┤
│ +operation()  │  │ -children     │
└───────────────┘  │ +add()        │
                   │ +remove()     │
                   │ +operation()  │
                   └───────────────┘

Java 实现

// 抽象组件
interface FileSystemComponent {
    void display();
}
​
// 叶子节点:文件
class File implements FileSystemComponent {
    private String name;
    public File(String name) { this.name = name; }
    @Override
    public void display() {
        System.out.println("File: " + name);
    }
}
​
// 组合节点:目录
class Directory implements FileSystemComponent {
    private String name;
    private List<FileSystemComponent> children = new ArrayList<>();
​
    public Directory(String name) { this.name = name; }
    public void add(FileSystemComponent component) {
        children.add(component);
    }
    @Override
    public void display() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent child : children) {
            child.display();
        }
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        Directory root = new Directory("root");
        root.add(new File("file1.txt"));
        
        Directory subDir = new Directory("sub");
        subDir.add(new File("file2.txt"));
        root.add(subDir);
        
        root.display(); // 输出目录树结构
    }
}

适用场景

  • 文件系统、UI 组件树
  • 需要统一处理部分与整体的场景

优缺点

  • 优点:简化客户端代码,支持递归组合。
  • 缺点:设计过于通用,可能违反接口隔离原则。

4. 装饰器模式(Decorator)

定义

动态地为对象添加额外职责,替代继承的扩展方式。

类图

┌───────────────────┐       ┌───────────────────┐
│ Component         │<>─────│ Decorator         │
├───────────────────┤       ├───────────────────┤
│ +operation()      │       │ -component        │
└───────────────────┘       │ +operation()      │
       △                     └─────────┬─────────┘
       │                               △
       │                               │
       ├───────────────────────────────┘
       │
┌───────────────┐       ┌───────────────────┐
│ ConcreteComp  │       │ ConcreteDecorator │
├───────────────┤       ├───────────────────┤
│ +operation()  │       │ +operation()      │
└───────────────┘       │ +addedBehavior()  │
                        └───────────────────┘

Java 实现

// 基础组件接口
interface Coffee {
    double getCost();
    String getDescription();
}
​
// 具体组件
class SimpleCoffee implements Coffee {
    @Override
    public double getCost() { return 2.0; }
    @Override
    public String getDescription() { return "Simple Coffee"; }
}
​
// 抽象装饰器
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    public double getCost() {
        return decoratedCoffee.getCost();
    }
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }
}
​
// 具体装饰器:加牛奶
class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
    @Override
    public String getDescription() {
        return super.getDescription() + ", Milk";
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();
        coffee = new MilkDecorator(coffee);
        System.out.println(coffee.getDescription() + " - $" + coffee.getCost());
        // 输出: Simple Coffee, Milk - $2.5
    }
}

适用场景

  • Java I/O 流(如 BufferedInputStream 包装 FileInputStream
  • 动态扩展对象功能

优缺点

  • 优点:避免类爆炸,运行时扩展。
  • 缺点:多层装饰增加复杂性。

5. 外观模式(Facade)

定义

为复杂子系统提供一个统一的简化接口。

类图

┌───────────────────┐       ┌───────────────────┐
│ Facade            │──────▶│ Subsystem Classes │
├───────────────────┤       ├───────────────────┤
│ +simpleMethod()   │       │ +operationA()     │
└───────────────────┘       │ +operationB()     │
                            └───────────────────┘

Java 实现

// 子系统类
class CPU {
    void start() { System.out.println("CPU启动"); }
}
class Memory {
    void load() { System.out.println("内存加载"); }
}
class HardDrive {
    void read() { System.out.println("硬盘读取"); }
}
​
// 外观类
class ComputerFacade {
    private CPU cpu;
    private Memory memory;
    private HardDrive hardDrive;
​
    public ComputerFacade() {
        this.cpu = new CPU();
        this.memory = new Memory();
        this.hardDrive = new HardDrive();
    }
​
    public void startComputer() {
        cpu.start();
        memory.load();
        hardDrive.read();
        System.out.println("计算机启动完成");
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        ComputerFacade computer = new ComputerFacade();
        computer.startComputer(); // 隐藏子系统复杂调用
    }
}

适用场景

  • 简化复杂库或框架的使用
  • 分层结构中的中间层设计

优缺点

  • 优点:降低客户端复杂度,解耦子系统。
  • 缺点:可能成为“上帝对象”,违反单一职责原则。

6. 享元模式(Flyweight)

定义

通过共享技术高效支持大量细粒度对象。

类图

┌───────────────────┐       ┌───────────────────┐
│ FlyweightFactory  │       │ Flyweight         │
├───────────────────┤       ├───────────────────┤
│ -cache: Map       │       │ +operation()      │
│ +getFlyweight()   │       └─────────┬─────────┘
└───────────────────┘                 △
                                      │
                               ┌───────────────┐
                               │ ConcreteFlyweight
                               ├───────────────┤
                               │ +operation()  │
                               └───────────────┘

Java 实现

// 享元接口
interface TreeType {
    void draw(int x, int y);
}
​
// 具体享元:树的类型(内部状态)
class ConcreteTreeType implements TreeType {
    private String name;
    private String color;
    
    public ConcreteTreeType(String name, String color) {
        this.name = name;
        this.color = color;
    }
    @Override
    public void draw(int x, int y) {
        System.out.printf("在(%d,%d)绘制%s色的%s树\n", x, y, color, name);
    }
}
​
// 享元工厂
class TreeFactory {
    private static Map<String, TreeType> cache = new HashMap<>();
    
    public static TreeType getTreeType(String name, String color) {
        String key = name + "_" + color;
        if (!cache.containsKey(key)) {
            cache.put(key, new ConcreteTreeType(name, color));
        }
        return cache.get(key);
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        TreeType oakGreen = TreeFactory.getTreeType("Oak", "Green");
        oakGreen.draw(10, 20); // 复用已有对象
    }
}

适用场景

  • 文本编辑器中的字符对象池
  • 游戏中的大量粒子效果

优缺点

  • 优点:大幅减少内存占用。
  • 缺点:增加对象状态管理复杂度(需区分内部/外部状态)。

7. 代理模式(Proxy)

定义

为其他对象提供代理以控制对该对象的访问。

类图

┌───────────────────┐       ┌───────────────────┐
│ Subject           │       │ Proxy             │
├───────────────────┤       ├───────────────────┤
│ +request()        │       │ -realSubject      │
└──────┬────────────┘       │ +request()        │
       △                     └─────────┬─────────┘
       │                               │
       │                       ┌───────▼───────┐
       │                       │ RealSubject   │
       │                       ├───────────────┤
       └───────────────────────┤ +request()    │
                               └───────────────┘

Java 实现

// 抽象主题
interface Image {
    void display();
}
​
// 真实主题
class RealImage implements Image {
    private String filename;
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }
    private void loadFromDisk() {
        System.out.println("加载图片: " + filename);
    }
    @Override
    public void display() {
        System.out.println("显示图片: " + filename);
    }
}
​
// 代理类
class ImageProxy implements Image {
    private RealImage realImage;
    private String filename;
    
    public ImageProxy(String filename) {
        this.filename = filename;
    }
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename); // 延迟加载
        }
        realImage.display();
    }
}
​
// 客户端调用
public class Client {
    public static void main(String[] args) {
        Image image = new ImageProxy("test.jpg");
        image.display(); // 第一次触发真实对象加载
    }
}

适用场景

  • 虚拟代理(延迟加载大对象)
  • 保护代理(权限控制)

优缺点

  • 优点:控制对象访问,增强安全性。
  • 缺点:可能引入性能开销。

总结

模式核心目标典型场景
适配器接口转换旧系统兼容
桥接分离抽象与实现多维度变化的系统
组合统一处理部分与整体树形结构管理
装饰器动态添加职责I/O流扩展
外观简化复杂子系统一键操作封装
享元共享细粒度对象游戏粒子系统
代理控制对象访问延迟加载、权限控制

最佳实践

  • 优先使用组合而非继承(如装饰器模式)。
  • 享元模式需明确区分内部/外部状态。
  • 代理模式可结合动态代理(如 Java 的 Proxy 类)实现更灵活的拦截。