3设计模式

结构型模式

类和对象的组合

AdapterBridgeCompositeDecoratorFacadeFlyweightProxy

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第3章:结构型模式(Structural Patterns)

一、导读

1.1 本章学习目标

第3章介绍 7 个结构型模式。结构型模式关注如何组合类和对象来形成更大的结构,它们让你在不改变现有类的情况下改变其行为或接口。

本章的核心学习目标包括:

  • 掌握 Adapter:让接口不兼容的类可以协同工作
  • 理解 Bridge:将抽象部分与实现部分分离
  • 学会 Composite:将对象组合成树形结构
  • 掌握 Decorator:动态地给对象添加职责
  • 理解 Facade:为子系统提供统一的接口
  • 了解 Flyweight:共享细粒度对象
  • 掌握 Proxy:为其他对象提供代理以控制访问

1.2 为什么本章如此重要

结构型模式解决的是组合与适配的问题。在真实系统中,你经常需要让已有的类协同工作,即使它们的接口不兼容;需要动态地添加功能,而不修改原有代码;需要为复杂的子系统提供简单的接口。结构型模式提供了这些问题的经过验证的解决方案。

1.3 核心问题引导

Adapter 与 Facade 有什么区别?

Decorator 与继承相比有什么优势?

Composite 模式如何简化树形结构的处理?

Bridge 模式与策略模式有什么关系?

Proxy 有哪些变体?各自解决什么问题?


二、核心概念详解

2.1 Adapter(适配器)

意图:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

java
// 适配器模式
interface MediaPlayer {
  void play(String audioFile);
}

class Mp3Player implements MediaPlayer {
  public void play(String audioFile) {
    System.out.println("Playing MP3: " + audioFile);
  }
}

// 第三方库的接口不兼容
class AdvancedMediaPlayer {
  void playVlc(String fileName) {
    System.out.println("Playing VLC: " + fileName);
  }
  void playMp4(String fileName) {
    System.out.println("Playing MP4: " + fileName);
  }
}

// 适配器:让不兼容的接口协同工作
class VlcAdapter implements MediaPlayer {
  private AdvancedMediaPlayer advancedPlayer = new AdvancedMediaPlayer();
  
  public void play(String audioFile) {
    advancedPlayer.playVlc(audioFile);
  }
}

class Mp4Adapter implements MediaPlayer {
  private AdvancedMediaPlayer advancedPlayer = new AdvancedMediaPlayer();
  
  public void play(String audioFile) {
    advancedPlayer.playMp4(audioFile);
  }
}

// 使用
MediaPlayer player = new VlcAdapter();
player.play("movie.vlc"); // 通过适配器调用不兼容的接口

2.2 Decorator(装饰器)

意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。

java
// 装饰器模式
interface Coffee {
  double cost();
  String description();
}

class SimpleCoffee implements Coffee {
  public double cost() { return 5; }
  public String description() { return "Simple coffee"; }
}

// 装饰器基类
abstract class CoffeeDecorator implements Coffee {
  protected Coffee coffee;
  
  CoffeeDecorator(Coffee coffee) {
    this.coffee = coffee;
  }
  
  public double cost() { return coffee.cost(); }
  public String description() { return coffee.description(); }
}

class MilkDecorator extends CoffeeDecorator {
  MilkDecorator(Coffee coffee) { super(coffee); }
  
  public double cost() { return coffee.cost() + 2; }
  public String description() { return coffee.description() + ", milk"; }
}

class SugarDecorator extends CoffeeDecorator {
  SugarDecorator(Coffee coffee) { super(coffee); }
  
  public double cost() { return coffee.cost() + 0.5; }
  public String description() { return coffee.description() + ", sugar"; }
}

// 使用:动态组合功能
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

System.out.println(coffee.description()); // "Simple coffee, milk, sugar"
System.out.println(coffee.cost()); // 7.5

2.3 Composite(组合)

意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

java
// 组合模式
interface FileSystemComponent {
  String getName();
  long getSize();
  void display(String indent);
}

class File implements FileSystemComponent {
  private String name;
  private long size;
  
  File(String name, long size) {
    this.name = name;
    this.size = size;
  }
  
  public String getName() { return name; }
  public long getSize() { return size; }
  public void display(String indent) {
    System.out.println(indent + name + " (" + size + " bytes)");
  }
}

class Directory implements FileSystemComponent {
  private String name;
  private List<FileSystemComponent> children = new ArrayList<>();
  
  Directory(String name) { this.name = name; }
  
  void add(FileSystemComponent component) {
    children.add(component);
  }
  
  public String getName() { return name; }
  
  public long getSize() {
    return children.stream().mapToLong(FileSystemComponent::getSize).sum();
  }
  
  public void display(String indent) {
    System.out.println(indent + name + "/");
    for (FileSystemComponent child : children) {
      child.display(indent + "  ");
    }
  }
}

// 使用:统一处理单个对象和组合对象
Directory root = new Directory("root");
Directory docs = new Directory("docs");
docs.add(new File("readme.txt", 1024));
docs.add(new File("guide.pdf", 2048));

root.add(docs);
root.add(new File("config.json", 512));

root.display(""); // 统一处理文件和目录
System.out.println("Total size: " + root.getSize());

2.4 Facade(外观)

意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

java
// 外观模式
class VideoConverter {
  String convert(String filename, String format) {
    return "Converted " + filename + " to " + format;
  }
}

class AudioExtractor {
  String extract(String filename) {
    return "Audio from " + filename;
  }
}

class BitrateReader {
  String read(String filename, String codec) {
    return "Reading " + filename + " with " + codec;
  }
}

// 外观:简化复杂子系统的使用
class ConversionFacade {
  void convertVideo(String filename, String format) {
    BitrateReader reader = new BitrateReader();
    String data = reader.read(filename, "h264");
    
    AudioExtractor audio = new AudioExtractor();
    String audioData = audio.extract(filename);
    
    VideoConverter converter = new VideoConverter();
    String result = converter.convert(data, format);
    
    System.out.println("Conversion complete: " + result);
  }
}

// 使用
ConversionFacade facade = new ConversionFacade();
facade.convertVideo("movie.mp4", "avi");

2.5 Proxy(代理)

意图:为其他对象提供一种代理以控制对这个对象的访问。

java
// 代理模式
interface Image {
  void display();
}

class RealImage implements Image {
  private String filename;
  
  RealImage(String filename) {
    this.filename = filename;
    loadFromDisk(); // 昂贵的操作
  }
  
  private void loadFromDisk() {
    System.out.println("Loading image: " + filename);
  }
  
  public void display() {
    System.out.println("Displaying: " + filename);
  }
}

// 代理:延迟创建昂贵对象
class ImageProxy implements Image {
  private String filename;
  private RealImage realImage;
  
  ImageProxy(String filename) {
    this.filename = filename; // 不立即加载
  }
  
  public void display() {
    if (realImage == null) {
      realImage = new RealImage(filename); // 延迟到需要时才创建
    }
    realImage.display();
  }
}

// 使用
Image image = new ImageProxy("photo.jpg"); // 不加载
image.display(); // 此时才加载并显示

三、实战案例

3.1 案例:使用装饰器模式构建数据流处理

java
interface DataSource {
  String readData();
  void writeData(String data);
}

class FileDataSource implements DataSource {
  private String filename;
  
  FileDataSource(String filename) { this.filename = filename; }
  
  public String readData() { return "raw data from " + filename; }
  public void writeData(String data) { System.out.println("Writing to file: " + data); }
}

abstract class DataSourceDecorator implements DataSource {
  protected DataSource wrappee;
  DataSourceDecorator(DataSource source) { this.wrappee = source; }
}

class EncryptionDecorator extends DataSourceDecorator {
  EncryptionDecorator(DataSource source) { super(source); }
  
  public String readData() {
    return decrypt(super.readData());
  }
  
  public void writeData(String data) {
    super.writeData(encrypt(data));
  }
  
  private String encrypt(String data) { return "[encrypted]" + data; }
  private String decrypt(String data) { return data.replace("[encrypted]", ""); }
}

class CompressionDecorator extends DataSourceDecorator {
  CompressionDecorator(DataSource source) { super(source); }
  
  public String readData() {
    return decompress(super.readData());
  }
  
  public void writeData(String data) {
    super.writeData(compress(data));
  }
  
  private String compress(String data) { return "[compressed]" + data; }
  private String decompress(String data) { return data.replace("[compressed]", ""); }
}

// 使用:灵活组合功能
DataSource source = new FileDataSource("data.txt");
source = new CompressionDecorator(source);
source = new EncryptionDecorator(source);

source.writeData("Hello World"); // 先加密再压缩再写入

四、本章小结

本章介绍了 7 个结构型模式:

Adapter:让接口不兼容的类可以协同工作

Bridge:将抽象部分与实现部分分离

Composite:将对象组合成树形结构

Decorator:动态地给对象添加职责

Facade:为子系统提供统一的接口

Flyweight:共享细粒度对象

Proxy:为其他对象提供代理以控制访问

结构型模式的核心思想是:通过组合来形成更大的结构,同时保持灵活性。


五、思考与练习

思考题

Adapter 和 Facade 的区别是什么?

Decorator 模式与策略模式有什么关系?

Composite 模式如何简化客户端代码?

Proxy 有哪些变体?各自的应用场景是什么?

编程练习

使用 Adapter 模式让一个旧的日志库适配新的日志接口。

使用 Decorator 模式实现一个支持多种格式的文本处理器。

使用 Composite 模式实现一个组织结构管理系统。


3/6结构型模
0%