Java 设计模式:结构型模式
结构型模式关注的是 类和对象的组合方式,它们提供了一种 灵活且可扩展的方式 来构建复杂系统。 这些模式主要通过 定义类和对象的结构 来解决系统设计中的 复杂性问题。
一、 结构型模式分类
结构型模式主要分为以下几类:
- 适配器模式 (Adapter):将一个类的接口转换成客户期望的另一个接口。适配器模式让你可以将现有的类和不兼容的类一起工作。
- 桥接模式 (Bridge):将抽象部分与实现部分分离,使它们可以独立变化。桥接模式可以用来 减少类之间的耦合,并 提高代码的可复用性。
- 组合模式 (Composite):将对象组合成树形结构,以表示"部分 - 整体" 的层次关系。组合模式可以让你 统一地处理单个对象和组合对象。
- 装饰器模式 (Decorator):动态地给一个对象添加额外的职责。装饰器模式可以让你 在不修改原有类的情况下,扩展对象的功能。
- 外观模式 (Facade):为子系统提供一个统一的接口,隐藏子系统的复杂性。外观模式可以让你 简化对子系统的调用,并 降低耦合度。
- 享元模式 (Flyweight):运用共享技术有效地支持大量细粒度的对象。享元模式可以让你 减少内存占用,并 提高性能。
- 代理模式 (Proxy):为其他对象提供一种代理以控制对这个对象的访问。代理模式可以让你 控制对对象的访问,并 实现延迟加载、安全控制等功能。
二、 结构型模式详解
1. 适配器模式 (Adapter)
定义: 将一个类的接口转换成客户期望的另一个接口。适配器模式让你可以将现有的类和不兼容的类一起工作。
分类:
- 类适配器: 使用继承来实现适配器,将目标接口继承下来,并实现源接口。
- 对象适配器: 使用组合来实现适配器,将目标对象作为成员变量,并实现源接口。
应用场景:
- 需要使用一个已有类的功能,但是它的接口不符合需求。
- 需要将多个接口统一成一个接口。
实例:
假设你需要使用一个第三方库的 LegacyClass
类,它使用的是老式的 LegacyInterface
接口,而你的程序需要使用的是 ModernInterface
接口。 你可以使用适配器模式来解决这个问题:
// LegacyInterface 接口
public interface LegacyInterface {
void legacyMethod();
}
// LegacyClass 类
public class LegacyClass implements LegacyInterface {
@Override
public void legacyMethod() {
System.out.println("调用 LegacyClass 的方法");
}
}
// ModernInterface 接口
public interface ModernInterface {
void modernMethod();
}
// 类适配器实现
public class ClassAdapter extends LegacyClass implements ModernInterface {
@Override
public void modernMethod() {
// 将现代方法调用转化为旧的方法调用
this.legacyMethod();
}
}
// 对象适配器实现
public class ObjectAdapter implements ModernInterface {
private LegacyClass legacyClass;
public ObjectAdapter(LegacyClass legacyClass) {
this.legacyClass = legacyClass;
}
@Override
public void modernMethod() {
// 将现代方法调用转化为旧的方法调用
legacyClass.legacyMethod();
}
}
// 使用适配器
public class Main {
public static void main(String[] args) {
// 使用类适配器
ClassAdapter adapter1 = new ClassAdapter();
adapter1.modernMethod();
// 使用对象适配器
ObjectAdapter adapter2 = new ObjectAdapter(new LegacyClass());
adapter2.modernMethod();
}
}
2. 桥接模式 (Bridge)
定义: 将抽象部分与实现部分分离,使它们可以独立变化。桥接模式可以用来 减少类之间的耦合,并 提高代码的可复用性。
应用场景:
- 当你希望 独立地修改抽象部分和实现部分 时。
- 当你希望 提供多个实现部分,以满足不同的需求时。
实例:
假设你需要设计一个图形绘制工具,它可以绘制不同的形状,并且可以支持不同的颜色。 你可以使用桥接模式来实现这个功能:
// 形状抽象类
public abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
// 圆形类
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("绘制一个" + color.getColorName() + "色的圆形");
}
}
// 正方形类
public class Square extends Shape {
public Square(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("绘制一个" + color.getColorName() + "色的正方形");
}
}
// 颜色抽象类
public abstract class Color {
public abstract String getColorName();
}
// 红色类
public class Red extends Color {
@Override
public String getColorName() {
return "红色";
}
}
// 蓝色类
public class Blue extends Color {
@Override
public String getColorName() {
return "蓝色";
}
}
// 使用桥接模式
public class Main {
public static void main(String[] args) {
// 创建红色圆形
Shape redCircle = new Circle(new Red());
redCircle.draw();
// 创建蓝色正方形
Shape blueSquare = new Square(new Blue());
blueSquare.draw();
}
}
3. 组合模式 (Composite)
定义: 将对象组合成树形结构,以表示"部分 - 整体" 的层次关系。组合模式可以让你 统一地处理单个对象和组合对象。
应用场景:
- 需要表示 部分 - 整体 的层次关系。
- 需要 统一地处理单个对象和组合对象。
实例:
假设你需要设计一个文件系统,它可以包含目录和文件,并且可以递归地遍历文件系统。 你可以使用组合模式来实现这个功能:
// 文件系统抽象类
public abstract class FileSystemItem {
public abstract String getName();
public abstract void printDetails();
public abstract FileSystemItem findItem(String name);
}
// 文件类
public class File extends FileSystemItem {
private String name;
public File(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void printDetails() {
System.out.println("文件: " + name);
}
@Override
public FileSystemItem findItem(String name) {
return name.equals(this.name) ? this : null;
}
}
// 目录类
public class Directory extends FileSystemItem {
private String name;
private List<FileSystemItem> items = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void printDetails() {
System.out.println("目录: " + name);
for (FileSystemItem item : items) {
item.printDetails();
}
}
@Override
public FileSystemItem findItem(String name) {
for (FileSystemItem item : items) {
if (item.getName().equals(name)) {
return item;
}
if (item instanceof Directory) {
FileSystemItem foundItem = ((Directory) item).findItem(name);
if (foundItem != null) {
return foundItem;
}
}
}
return null;
}
public void addItem(FileSystemItem item) {
items.add(item);
}
}
// 使用组合模式
public class Main {
public static void main(String[] args) {
// 创建文件系统
Directory root = new Directory("root");
Directory dir1 = new Directory("dir1");
Directory dir2 = new Directory("dir2");
File file1 = new File("file1.txt");
File file2 = new File("file2.txt");
// 构建树形结构
root.addItem(dir1);
root.addItem(dir2);
dir1.addItem(file1);
dir2.addItem(file2);
// 打印文件系统细节
root.printDetails();
// 查找文件
FileSystemItem foundFile = root.findItem("file2.txt");
if (foundFile != null) {
System.out.println("找到文件: " + foundFile.getName());
}
}
}
4. 装饰器模式 (Decorator)
定义: 动态地给一个对象添加额外的职责。装饰器模式可以让你 在不修改原有类的情况下,扩展对象的功能。
应用场景:
- 需要 动态地扩展对象的功能,而不改变原有类的代码。
- 需要 提供多种装饰功能,并可以自由组合。
实例:
假设你需要设计一个咖啡机,它可以制作各种咖啡,并且可以添加不同的配料,比如糖、牛奶、香草等。 你可以使用装饰器模式来实现这个功能:
// 咖啡抽象类
public abstract class Coffee {
public abstract String getDescription();
public abstract double cost();
}
// 黑咖啡类
public class Espresso extends Coffee {
@Override
public String getDescription() {
return "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
// 装饰器抽象类
public abstract class CoffeeDecorator extends Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public String getDescription() {
return coffee.getDescription();
}
@Override
public double cost() {
return coffee.cost();
}
}
// 糖装饰器类
public class Sugar extends CoffeeDecorator {
public Sugar(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 糖";
}
@Override
public double cost() {
return coffee.cost() + 0.25;
}
}
// 牛奶装饰器类
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}
@Override
public String getDescription() {
return coffee.getDescription() + " + 牛奶";
}
@Override
public double cost() {
return coffee.cost() + 0.50;
}
}
// 使用装饰器模式
public class Main {
public static void main(String[] args) {
// 创建黑咖啡
Coffee espresso = new Espresso();
System.out.println(espresso.getDescription() + " $" + espresso.cost());
// 添加糖和牛奶
Coffee coffeeWithSugarAndMilk = new Milk(new Sugar(espresso));
System.out.println(coffeeWithSugarAndMilk.getDescription() + " $" + coffeeWithSugarAndMilk.cost());
}
}
5. 外观模式 (Facade)
定义: 为子系统提供一个统一的接口,隐藏子系统的复杂性。外观模式可以让你 简化对子系统的调用,并 降低耦合度。
应用场景:
- 需要 简化对复杂子系统的调用。
- 需要 降低子系统与其他模块的耦合度。
实例:
假设你需要设计一个数据库系统,它包含多个组件,比如连接池、数据库操作、事务管理等。 你可以使用外观模式来简化对数据库系统的调用:
// 数据库子系统接口
public interface DatabaseSubsystem {
void connect();
void executeQuery(String query);
void commitTransaction();
void rollbackTransaction();
void disconnect();
}
// 数据库子系统实现类
public class DatabaseSubsystemImpl implements DatabaseSubsystem {
// 连接池
private ConnectionPool connectionPool;
// 数据库操作
private DatabaseOperations databaseOperations;
// 事务管理
private TransactionManager transactionManager;
public DatabaseSubsystemImpl(ConnectionPool connectionPool, DatabaseOperations databaseOperations, TransactionManager transactionManager) {
this.connectionPool = connectionPool;
this.databaseOperations = databaseOperations;
this.transactionManager = transactionManager;
}
@Override
public void connect() {
connectionPool.getConnection();
}
@Override
public void executeQuery(String query) {
databaseOperations.executeQuery(query);
}
@Override
public void commitTransaction() {
transactionManager.commitTransaction();
}
@Override
public void rollbackTransaction() {
transactionManager.rollbackTransaction();
}
@Override
public void disconnect() {
connectionPool.releaseConnection();
}
}
// 数据库外观类
public class DatabaseFacade {
private DatabaseSubsystem databaseSubsystem;
public DatabaseFacade(DatabaseSubsystem databaseSubsystem) {
this.databaseSubsystem = databaseSubsystem;
}
public void executeAndCommit(String query) {
databaseSubsystem.connect();
databaseSubsystem.executeQuery(query);
databaseSubsystem.commitTransaction();
databaseSubsystem.disconnect();
}
public void executeAndRollback(String query) {
databaseSubsystem.connect();
databaseSubsystem.executeQuery(query);
databaseSubsystem.rollbackTransaction();
databaseSubsystem.disconnect();
}
}
// 使用数据库外观
public class Main {
public static void main(String[] args) {
// 创建数据库子系统
DatabaseSubsystem databaseSubsystem = new DatabaseSubsystemImpl(new ConnectionPool(), new DatabaseOperations(), new TransactionManager());
// 创建数据库外观
DatabaseFacade databaseFacade = new DatabaseFacade(databaseSubsystem);
// 使用外观执行查询并提交事务
databaseFacade.executeAndCommit("SELECT * FROM users");
// 使用外观执行查询并回滚事务
databaseFacade.executeAndRollback("UPDATE users SET name = 'John'");
}
}
6. 享元模式 (Flyweight)
定义: 运用共享技术有效地支持大量细粒度的对象。享元模式可以让你 减少内存占用,并 提高性能。
应用场景:
- 需要 创建大量相同或相似的对象。
- 需要 减少内存占用,并 提高性能。
实例:
假设你需要设计一个游戏,它需要创建大量的树木,而这些树木可能只是颜色和大小不同,其他属性都相同。 你可以使用享元模式来实现这个功能:
// 树抽象类
public abstract class Tree {
public abstract void draw(int x, int y);
}
// 具体树类
public class ConcreteTree implements Tree {
private String color;
private int size;
public ConcreteTree(String color, int size) {
this.color = color;
this.size = size;
}
@Override
public void draw(int x, int y) {
System.out.println("绘制一棵 " + color + " 色的树,大小为 " + size + ",位置为 (" + x + ", " + y + ")");
}
}
// 树工厂类
public class TreeFactory {
private Map<String, Tree> trees = new HashMap<>();
public Tree getTree(String color, int size) {
String key = color + "_" + size;
Tree tree = trees.get(key);
if (tree == null) {
tree = new ConcreteTree(color, size);
trees.put(key, tree);
}
return tree;
}
}
// 使用享元模式
public class Main {
public static void main(String[] args) {
// 创建树工厂
TreeFactory treeFactory = new TreeFactory();
// 创建多棵树
Tree tree1 = treeFactory.getTree("绿色", 10);
Tree tree2 = treeFactory.getTree("绿色", 10);
Tree tree3 = treeFactory.getTree("蓝色", 5);
// 绘制树
tree1.draw(10, 20);
tree2.draw(30, 40);
tree3.draw(50, 60);
}
}
7. 代理模式 (Proxy)
定义: 为其他对象提供一种代理以控制对这个对象的访问。代理模式可以让你 控制对对象的访问,并 实现延迟加载、安全控制等功能。
分类:
- 远程代理: 为位于不同地址空间的对象提供本地代理。
- 虚拟代理: 为需要较长时间创建的对象提供代理,延迟创建真实对象。
- 保护代理: 为真实对象提供安全控制,限制对对象的访问。
应用场景:
- 需要 控制对对象的访问。
- 需要 实现延迟加载、安全控制等功能。
实例:
假设你需要设计一个图片加载器,它可以加载网络图片。 你可以使用代理模式来实现这个功能,并实现延迟加载:
// 图片接口
public interface Image {
void display();
}
// 真实图片类
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}
private void loadFromDisk(String fileName) {
System.out.println("从磁盘加载 " + fileName + " 图片...");
// 这里模拟图片加载过程
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("图片加载完成");
}
@Override
public void display() {
System.out.println("显示 " + fileName + " 图片");
}
}
// 图片代理类
public class ProxyImage implements Image {
private String fileName;
private RealImage realImage;
public ProxyImage(String fileName) {
this.fileName = fileName;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}
}
// 使用代理模式
public class Main {
public static void main(String[] args) {
// 创建图片代理
Image image = new ProxyImage("photo.jpg");
// 首次调用 display(),会加载图片
image.display();
// 再次调用 display(),图片已经加载,直接显示
image.display();
}
}
三、 总结
结构型模式是设计模式中非常重要的组成部分,它们提供了一种灵活且可扩展的方式来构建复杂系统。 在实际开发中,我们可以根据具体需求选择合适的结构型模式来 提高代码的可读性、可维护性和可扩展性。
需要注意的是,设计模式不是万能的,应该根据实际情况选择合适的模式,不要为了使用模式而使用模式。