设计模式的三驾马车:创建型、结构型与行为型模式解析
在软件工程领域,设计模式是解决常见设计问题的经典、可复用的方案。它们如同建筑大师的蓝图,为开发者提供了经过验证的最佳实践,使得代码更加健壮、灵活且易于维护。为了系统地学习和应用这些模式,GoF(Gang of Four)在其开创性著作《设计模式:可复用面向对象软件的基础》中,将23种经典模式分为了三大类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns) 和 行为型模式(Behavioral Patterns)。本文将带你快速了解这三大分类的核心思想与典型应用。
一、创建型模式(Creational Patterns)
核心思想
创建型模式的核心关注点是如何创建对象。它们将对象的创建过程抽象出来,使得系统在创建对象时具有更大的灵活性,不再依赖于对象的具体实现类。这类模式通过“解耦”对象的实例化过程,帮助我们应对对象创建时的复杂性,尤其是在需要应对变化时,比如需要创建哪些对象、如何组合它们以及如何表示创建过程。
典型模式与应用场景
创建型模式主要包括:工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)、建造者模式(Builder)、原型模式(Prototype) 和 单例模式(Singleton)。
- 工厂方法模式:定义一个用于创建对象的接口,但让子类决定实例化哪一个类。它常用于框架设计中,让框架定义通用结构,而由用户决定具体的实现。
- 抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。这在需要确保一系列相关产品一起使用时非常有用,例如创建一整套跨平台的UI控件。
- 建造者模式:将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。它非常适合构建那些需要通过多个步骤、有很多配置选项的复杂对象(如一份订餐订单、一台电脑的配置)。
- 原型模式:通过复制一个已存在的原型实例来创建新的对象,而不是通过
new关键字。这在创建对象成本较高(如需要从数据库加载大量数据)或系统需要动态配置运行时的对象时非常高效。 - 单例模式:确保一个类只有一个实例,并提供一个全局访问点。它被广泛用于需要全局唯一性的场景,如线程池、缓存、日志对象、对话框、设备驱动程序等。
简单代码示例:单例模式 (Java)
public class Singleton {
// 持有私有静态实例,防止被引用
private static volatile Singleton instance = null;
// 私有构造方法,防止外部实例化
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) { // 加锁
if (instance == null) { // 第二次检查(双重检查锁定)
instance = new Singleton();
}
}
}
return instance;
}
}
二、结构型模式(Structural Patterns)
核心思想
结构型模式的核心关注点是如何组合类和对象以形成更大的结构。它们通过继承和组合的机制,将不同的类或对象组织起来,以满足新的功能需求。这类模式的重点在于简化系统的结构设计,确保当系统的一部分发生变化时,不会影响到其他部分,从而增强系统的可扩展性和可维护性。
典型模式与应用场景
结构型模式主要包括:适配器模式(Adapter)、桥接模式(Bridge)、组合模式(Composite)、装饰器模式(Decorator)、外观模式(Facade)、享元模式(Flyweight) 和 代理模式(Proxy)。
- 适配器模式:将一个类的接口转换成客户希望的另外一个接口。它就像是一个转接头,让原本由于接口不兼容而不能一起工作的类可以一起工作,常用于集成第三方库或遗留代码。
- 桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。它通过组合代替继承,避免了继承层次的爆炸,常用于图形和窗口系统,以及需要多维度变化的场景。
- 组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。它使得用户对单个对象和组合对象的使用具有一致性,常用于表示文件系统、UI组件树等。
- 装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,它比生成子类更为灵活。它是继承的一个替代方案,常用于Java I/O流的设计中。
- 外观模式:为子系统中的一组接口提供一个一致的、更高层次的界面。它定义了一个高层接口,使得子系统更容易使用,常用于简化复杂库或API的调用。
- 享元模式:运用共享技术有效地支持大量细粒度的对象。它通过共享相同的状态来减少内存消耗,常用于游戏开发中的大量粒子、字符等场景。
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问。它在客户端和目标对象之间引入了一个中间层,常用于实现延迟加载、访问控制、日志记录等。
简单代码示例:适配器模式 (Java)
假设我们有一个旧的LegacyPrinter类,它有一个printDocument()方法,但新的客户端代码期望调用print()方法。
// 旧的不兼容的类
class LegacyPrinter {
public void printDocument() {
System.out.println("Printing a document...");
}
}
// 客户端期望的接口
interface Printer {
void print();
}
// 适配器类
class PrinterAdapter implements Printer {
private LegacyPrinter legacyPrinter;
public PrinterAdapter(LegacyPrinter legacyPrinter) {
this.legacyPrinter = legacyPrinter;
}
@Override
public void print() {
legacyPrinter.printDocument(); // 适配过程
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Printer printer = new PrinterAdapter(new LegacyPrinter());
printer.print(); // 输出:Printing a document...
}
}
三、行为型模式(Behavioral Patterns)
核心思想
行为型模式的核心关注点是对象之间的职责分配与通信。它们不仅描述对象或类的模式,还描述它们之间的通信模式。这类模式旨在识别对象之间常见的通信方式,并将这些方式抽象出来,使得通信更加灵活、松耦合,从而让系统在对象交互方面更容易被扩展和改变。
典型模式与应用场景
行为型模式主要包括:责任链模式(Chain of Responsibility)、命令模式(Command)、解释器模式(Interpreter)、迭代器模式(Iterator)、中介者模式(Mediator)、备忘录模式(Memento)、观察者模式(Observer)、状态模式(State)、策略模式(Strategy)、模板方法模式(Template Method) 和 访问者模式(Visitor)。
- 责任链模式:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。常用于审批流程、事件处理等。
- 命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,支持请求的排队、记录、撤销等操作。常用于GUI的按钮命令、事务管理。
- 迭代器模式:提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。它是Java集合框架的基石。
- 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。它是MVC架构、事件驱动系统的核心。
- 状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。它常用于实现状态机,如游戏中的角色状态、订单状态流转。
- 策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。它让算法独立于使用它的客户而变化,常用于替换大量的
if-else或switch-case语句。 - 模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。它使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,是代码复用的基本技术。
简单代码示例:策略模式 (Java)
假设我们需要实现一个计算器,支持不同的计算策略(加、减、乘)。
```