第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(适配器)
意图:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
// 适配器模式
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(装饰器)
意图:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。
// 装饰器模式
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.52.3 Composite(组合)
意图:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
// 组合模式
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(外观)
意图:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
// 外观模式
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(代理)
意图:为其他对象提供一种代理以控制对这个对象的访问。
// 代理模式
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 案例:使用装饰器模式构建数据流处理
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 模式实现一个组织结构管理系统。