设计模式专栏(三):结构型设计模式(Structural Design Patterns)(关注“Beyond Code 程序员”订阅号查看更多文章)
模式介绍
结构型设计模式关注的是如何将类或对象组合成更大的结构,从而实现更高效、更灵活的系统结构。它们通过继承或组合方式,使得不同类之间的关系更加清晰,方便代码复用和扩展。
结构型模式可以帮助我们:
- 简化类或对象之间的关系
- 提高代码的可维护性和可扩展性
- 通过组合动态地改变对象的功能或结构
主要包含六种模式(常见经典结构型模式)
- 适配器模式(Adapter)
- 桥接模式(Bridge)
- 装饰器模式(Decorator)
- 代理模式(Proxy)
- 外观模式(Facade)
- 组合模式(Composite)
- 享元模式(Flyweight Pattern)
⚠️ 提示:设计模式是一种编程思想,其实现方式并非唯一。本文示例代码仅作为参考,并不意味着设计模式必须按照示例中的方式实现。
1. 适配器模式(Adapter Pattern)
模式解释
适配器模式核心思想是:
将一个类的接口转换成客户希望的另一个接口,使原本接口不兼容的类可以一起工作。
适用场景:
- 现有类接口不兼容,想复用已有类
- 系统需要使用一些现有类,但接口不符合系统需求
代码示例(Java,类适配器,含测试代码)
// 目标接口
interface Target {
void request();
}
// 需要适配的类(被适配者,该类的逻辑不适合现有需求)
class Adaptee {
public void specificRequest() {
System.out.println("被适配者的方法执行");
}
}
// 适配器类,通过继承实现类适配器
class Adapter extends Adaptee implements Target {
public void request() {
// 适配调用被适配者的方法,可以在调用被适配者方法之前处理符合现在需求的业务逻辑
doSomething();
specificRequest();
}
private void doSomething() {
System.out.println("新的业务逻辑");
}
}
// 测试代码
public class AdapterDemo {
public static void main(String[] args) {
Target target = new Adapter();
target.request();
}
}
运行效果
新的业务逻辑
被适配者的方法执行
总结
优点:
- 提高了类的复用性
- 使得接口不兼容的类可以协同工作
缺点:
- 适配器过多可能增加系统复杂度
- 类适配器只能继承一个类,不支持多继承(Java限制)
2. 桥接模式(Bridge Pattern)
模式解释
桥接模式核心思想是:
将抽象部分与它的实现部分分离,使它们都可以独立变化。
适用场景:
- 抽象和实现都可能扩展,且不想使用继承绑定在一起
- 需要在不同维度上扩展系统
代码示例(Java,含注释+测试代码)
// 实现化接口
interface Implementor {
void operationImpl();
}
// 具体实现A
class ConcreteImplementorA implements Implementor {
public void operationImpl() {
System.out.println("具体实现A的操作");
}
}
// 具体实现B
class ConcreteImplementorB implements Implementor {
public void operationImpl() {
System.out.println("具体实现B的操作");
}
}
// 抽象类,持有实现化接口引用
abstract class Abstraction {
protected Implementor implementor;
protected Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
// 扩充抽象类
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
public void operation() {
System.out.print("扩充抽象中的操作:");
implementor.operationImpl();
}
}
// 测试代码
public class BridgeDemo {
public static void main(String[] args) {
Implementor implA = new ConcreteImplementorA();
Abstraction abs1 = new RefinedAbstraction(implA);
abs1.operation();
Implementor implB = new ConcreteImplementorB();
Abstraction abs2 = new RefinedAbstraction(implB);
abs2.operation();
}
}
运行效果
扩充抽象中的操作:具体实现A的操作
扩充抽象中的操作:具体实现B的操作
总结
优点:
- 抽象与实现分离,易于扩展
- 提高系统灵活性,减少继承带来的复杂性
缺点:
- 增加系统的理解和设计难度
- 需要正确识别抽象与实现部分
3. 装饰器模式(Decorator Pattern)
模式解释
装饰器模式核心思想是:
动态地给对象添加额外职责,而不会影响其他对象。
适用场景:
- 需要在不改变原始类的情况下扩展功能
- 需要动态、灵活地添加功能
代码示例(Java,含注释+测试代码)
// 抽象组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
public void operation() {
System.out.println("具体组件的操作");
}
}
// 抽象装饰类,实现组件接口,持有组件引用
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
// 具体装饰A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("具体装饰A的附加操作");
}
}
// 测试代码
public class DecoratorDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decorator = new ConcreteDecoratorA(component);
decorator.operation();
}
}
运行效果
具体组件的操作
具体装饰A的附加操作
总结
优点:
- 比继承更灵活,动态扩展功能
- 遵循开闭原则,不修改原有代码
缺点:
- 装饰层次多时,系统复杂,调试困难
- 需要设计良好的组件和装饰接口
4. 代理模式(Proxy Pattern)
模式解释
代理模式核心思想是:
为其他对象提供一个代理以控制对这个对象的访问。
适用场景:
- 控制对象访问权限
- 远程代理、虚拟代理、缓存代理等需求
- 需要在访问对象前后添加额外操作
代码示例(Java,含注释+测试代码)
// 抽象主题接口
interface Subject {
void request();
}
// 真实主题类
class RealSubject implements Subject {
public void request() {
System.out.println("真实主题的请求处理");
}
}
// 代理类,控制对真实主题的访问
class Proxy implements Subject {
private RealSubject realSubject;
public Proxy() {
this.realSubject = new RealSubject();
}
public void request() {
System.out.println("代理开始处理请求...");
realSubject.request();
System.out.println("代理完成请求处理");
}
}
// 测试代码
public class ProxyDemo {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
运行效果
代理开始处理请求...
真实主题的请求处理
代理完成请求处理
总结
优点:
- 控制真实对象访问,增强功能
- 可实现远程访问、延迟加载、权限控制等
缺点:
- 代理模式会增加系统层次,影响效率
- 设计和维护复杂度提升
5. 外观模式(Facade Pattern)
模式解释
外观模式核心思想是:
为子系统中的一组接口提供一个统一的高层接口,使子系统更易使用。
适用场景:
- 复杂系统需要简化调用接口
- 需要为多个子系统提供统一入口
代码示例(Java,含注释+测试代码)
// 子系统A
class SubSystemA {
public void operationA() {
System.out.println("子系统A操作");
}
}
// 子系统B
class SubSystemB {
public void operationB() {
System.out.println("子系统B操作");
}
}
// 外观类
class Facade {
private SubSystemA a = new SubSystemA();
private SubSystemB b = new SubSystemB();
public void operation() {
System.out.println("外观角色调用子系统操作:");
a.operationA();
b.operationB();
}
}
// 测试代码
public class FacadeDemo {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
运行效果
外观角色调用子系统操作:
子系统A操作
子系统B操作
总结
优点:
- 简化复杂系统调用
- 提高系统模块间松耦合
缺点:
- 不符合开闭原则,增加新子系统需要修改外观类
- 不能很好地限制子系统功能暴露
6. 组合模式(Composite Pattern)
模式解释
组合模式核心思想是:
将对象组合成树形结构以表示“部分-整体”的层次结构,使客户端对单个对象和组合对象的使用具有一致性。
适用场景:
- 需要表示对象的部分-整体层次结构
- 希望客户端忽略组合对象和单个对象的差异
代码示例(Java,含注释+测试代码)
import java.util.ArrayList;
import java.util.List;
// 抽象组件
abstract class Component {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract void add(Component c);
public abstract void remove(Component c);
public abstract void display(int depth);
}
// 叶子节点
class Leaf extends Component {
public Leaf(String name) {
super(name);
}
public void add(Component c) {
System.out.println("叶子节点不能添加子节点");
}
public void remove(Component c) {
System.out.println("叶子节点没有子节点");
}
public void display(int depth) {
System.out.println("-".repeat(depth) + name);
}
}
// 树枝节点
class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public void display(int depth) {
System.out.println("-".repeat(depth) + name);
for (Component c : children) {
c.display(depth + 2);
}
}
}
// 测试代码
public class CompositeDemo {
public static void main(String[] args) {
Composite root = new Composite("根节点");
Composite branch1 = new Composite("分支1");
Composite branch2 = new Composite("分支2");
Leaf leaf1 = new Leaf("叶子1");
Leaf leaf2 = new Leaf("叶子2");
Leaf leaf3 = new Leaf("叶子3");
root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch1.add(leaf2);
branch2.add(leaf3);
root.display(1);
}
}
运行效果
-根节点
---分支1
-----叶子1
-----叶子2
---分支2
-----叶子3
总结
优点:
- 客户端调用简单,统一处理单个对象和组合对象
- 更容易添加新类型的组件
缺点:
- 设计较复杂,涉及递归结构
- 可能使设计中出现过多细粒度对象,增加系统复杂度
7. 享元模式(Flyweight Pattern)
模式解释
享元模式的核心思想是:
通过共享技术有效地支持大量细粒度对象,减少内存占用,提高性能。
享元模式通过将对象的内部状态(可共享部分) 和外部状态(不可共享部分) 分离,实现对象复用。
适用场景
- 系统中存在大量相似对象,导致内存开销大
- 对象的大部分状态可以外部化,进行共享
- 需要大量对象但又限制内存消耗的场合,如文本编辑器字符对象、图形对象等
代码示例(Java,含注释+测试代码)
import java.util.HashMap;
import java.util.Map;
// 抽象享元接口
interface Flyweight {
void operation(String extrinsicState);
}
// 具体享元类
class ConcreteFlyweight implements Flyweight {
private final String intrinsicState; // 内部状态(共享部分)
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
public void operation(String extrinsicState) {
System.out.println("内部状态:" + intrinsicState + ", 外部状态:" + extrinsicState);
}
}
// 享元工厂,管理享元对象的共享
class FlyweightFactory {
private Map<String, Flyweight> flyweightMap = new HashMap<>();
public Flyweight getFlyweight(String key) {
if (!flyweightMap.containsKey(key)) {
flyweightMap.put(key, new ConcreteFlyweight(key));
System.out.println("创建新的享元:" + key);
} else {
System.out.println("使用已有享元:" + key);
}
return flyweightMap.get(key);
}
}
// 测试代码
public class FlyweightDemo {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight fw1 = factory.getFlyweight("A");
fw1.operation("第一次调用");
Flyweight fw2 = factory.getFlyweight("B");
fw2.operation("第二次调用");
Flyweight fw3 = factory.getFlyweight("A");
fw3.operation("第三次调用");
// 验证共享对象
System.out.println("fw1 和 fw3 是同一个对象吗?" + (fw1 == fw3));
}
}
运行效果
创建新的享元:A
内部状态:A, 外部状态:第一次调用
创建新的享元:B
内部状态:B, 外部状态:第二次调用
使用已有享元:A
内部状态:A, 外部状态:第三次调用
fw1 和 fw3 是同一个对象吗?true
总结
优点:
- 大幅减少对象数量,节省内存
- 提高系统性能
缺点:
- 设计复杂,需区分内部状态和外部状态
- 共享对象可能导致程序逻辑复杂,易出错
📌 下一篇: 《设计模式专栏( 四):行为型模式全解》
扫码关注订阅号