设计模式专栏(三):结构型设计模式(Structural Design Patterns)

63 阅读9分钟

设计模式专栏(三):结构型设计模式(Structural Design Patterns)(关注“Beyond Code 程序员”订阅号查看更多文章)

模式介绍

结构型设计模式关注的是如何将类或对象组合成更大的结构,从而实现更高效、更灵活的系统结构。它们通过继承或组合方式,使得不同类之间的关系更加清晰,方便代码复用和扩展。

结构型模式可以帮助我们:

  • 简化类或对象之间的关系
  • 提高代码的可维护性和可扩展性
  • 通过组合动态地改变对象的功能或结构

主要包含六种模式(常见经典结构型模式)

  1. 适配器模式(Adapter)
  2. 桥接模式(Bridge)
  3. 装饰器模式(Decorator)
  4. 代理模式(Proxy)
  5. 外观模式(Facade)
  6. 组合模式(Composite)
  7. 享元模式(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

总结

优点:

  • 大幅减少对象数量,节省内存
  • 提高系统性能

缺点:

  • 设计复杂,需区分内部状态和外部状态
  • 共享对象可能导致程序逻辑复杂,易出错

📌 下一篇《设计模式专栏( 四):行为型模式全解》


扫码关注订阅号

Beyond Code 程序员公众号二维码.jpg