设计模式(Design Patterns)是指在软件设计中,针对特定问题的最佳实践解决方案。它们是经过反复验证的,旨在提升软件的可重用性、可维护性和灵活性。设计模式通常分为三大类:创建型模式、结构型模式和行为型模式。以下是常见的23种设计模式及其简介:
一、创建型模式(Creational Patterns)
1. 单例模式(Singleton Pattern)
作用:
确保一个类只有一个实例,并提供全局访问点。
优点:
- 可以确保某个类在整个程序中只有一个实例,避免内存浪费。
- 适合管理共享资源(如数据库连接池、配置文件读取等)。
缺点:
- 隐藏了类的实例化过程,难以进行单元测试。
- 如果不小心实现,可能会导致线程安全问题。
常见应用场景:
- 配置管理器、日志管理器、数据库连接池。
2. 工厂方法模式(Factory Method Pattern)
作用:
定义一个创建对象的接口,但由子类决定实例化哪一个类。
优点:
- 将对象的创建过程延迟到子类,符合开闭原则。
- 可以避免复杂的构造函数和依赖。
缺点:
- 需要大量的子类来实现具体的工厂方法。
- 增加了系统的复杂性。
常见应用场景:
- UI组件库中的不同按钮、操作系统中的不同UI元素。
3. 抽象工厂模式(Abstract Factory Pattern)
作用:
提供一个接口,用于创建相关或依赖对象的家族,而无需指定具体类。
优点:
- 将多个产品族的创建逻辑集中管理,避免产品家族之间的耦合。
- 符合开闭原则,可以在不修改代码的情况下增加新的产品族。
缺点:
- 增加了系统的复杂度,使用时需要额外的工厂类。
- 如果产品家族较多,可能会导致工厂类数量暴增。
常见应用场景:
- 跨平台UI组件库、操作系统的硬件抽象层。
4. 建造者模式(Builder Pattern)
作用:
使用多个简单的对象一步步构建成一个复杂的对象。
优点:
- 客户端不需要了解构建细节,简化了对象的创建过程。
- 可以使用不同的建造方式来生成不同类型的对象。
缺点:
- 对于复杂对象的构建来说,可能需要很多的“建造者”类,导致代码冗长。
- 不适合在对象很简单时使用。
常见应用场景:
- HTML文档生成、复杂对象的序列化(如XML、JSON)。
5. 原型模式(Prototype Pattern)
作用:
通过复制现有的实例来创建新的对象,而不是通过构造器创建。
优点:
- 可以通过复制现有对象,避免重复创建对象的开销。
- 对象的创建和初始化过程较为复杂时,原型模式能够减少构建的复杂性。
缺点:
- 需要为对象定义“克隆”接口。
- 对象中的引用类型成员可能导致浅拷贝问题。
常见应用场景:
- 需要重复创建大量相似对象时(如图形对象、数据库记录的复制)。
二、结构型模式(Structural Patterns)
1. 适配器模式(Adapter Pattern)
作用:
将一个类的接口转换成客户端期望的接口,使得两个不兼容的接口可以工作。
优点:
- 适配器模式可以帮助老代码与新代码兼容。
- 提高系统的灵活性,增加可重用性。
缺点:
- 增加了一层额外的接口,可能导致系统结构复杂化。
- 如果接口差异过大,适配器会变得很复杂。
常见应用场景:
- 旧系统与新系统的接口适配,第三方库接口的适配。
2. 桥接模式(Bridge Pattern)
作用:
通过将抽象部分和实现部分分离,使得两者可以独立变化。
优点:
- 提高了系统的灵活性,允许独立变化的部分不再耦合。
- 遵循了开闭原则,方便扩展。
缺点:
- 需要在系统中增加更多的类。
- 如果抽象层次过多,可能导致设计复杂。
常见应用场景:
- 图形绘制系统,设备驱动程序。
3. 组合模式(Composite Pattern)
作用:
通过递归组合来构建对象的树形结构,从而使得客户端可以像处理单个对象一样处理组合对象。
优点:
- 客户端可以一致地对待单个对象和组合对象。
- 增加了系统的可扩展性,支持新的组件类型。
缺点:
- 设计较为复杂,尤其是当树形结构过深时。
- 很难控制所有组合对象的行为。
常见应用场景:
- 文件系统(文件夹和文件结构)、GUI界面组件。
4. 装饰器模式(Decorator Pattern)
作用:
动态地给对象添加额外的功能,而不改变其结构。
优点:
- 通过装饰器,可以在不修改现有类的情况下增强功能。
- 装饰器可以灵活叠加,避免了子类过多的问题。
缺点:
- 增加了系统的复杂度,尤其是当装饰器层数较多时。
- 装饰器链可能会导致性能开销。
常见应用场景:
- Java I/O中的装饰器(例如:BufferedReader、FileReader等组合)。
5. 外观模式(Facade Pattern)
作用:
为复杂的子系统提供一个统一的接口,使得子系统的使用更简便。
优点:
- 降低了系统的复杂度,通过简单的接口隐藏复杂的实现。
- 促进了子系统之间的解耦。
缺点:
- 如果过度依赖外观类,可能会忽略子系统中有用的功能。
- 系统扩展性差,所有功能都通过外观类暴露。
常见应用场景:
- 家庭影院、复杂框架的封装、第三方API的简化接口。
6. 享元模式(Flyweight Pattern)
作用:
通过共享对象来有效地支持大量的细粒度对象,减少内存占用。
优点:
- 可以节省大量内存,提高系统性能。
- 通过共享的方式,避免重复创建相同的对象。
缺点:
- 享元模式要求对象不可变,无法灵活修改。
- 需要管理共享对象池,增加了复杂度。
常见应用场景:
- 游戏中相同类型的对象(如敌人、子弹)共享资源。
7. 代理模式(Proxy Pattern)
作用:
为其他对象提供代理,以控制对对象的访问。
优点:
-
代理可以为真实对象提供保护,延迟加载等功能。
-
增加了系统的灵活性和控制性。
缺点:
-
增加了代码复杂度和层次。
-
代理的使用可能会影响性能。
常见应用场景:
-
远程代理(远程服务调用)、虚拟代理(延迟加载)、保护代理(权限控制)。
三、行为型模式(Behavioral Patterns)
1. 责任链模式(Chain of Responsibility Pattern)
作用:
通过将请求沿着处理链传递,直到有对象处理它为止。
优点:
- 责任链模式可以避免多个处理器之间的硬耦合,增加灵活性。
- 可以动态地增加处理环节。
缺点:
- 不保证请求最终能被处理,可能导致请求失败。
- 责任链过长时可能导致性能问题。
常见应用场景:
- 日志记录、审批流程、过滤器链。
2. 命令模式(Command Pattern)
作用:
将请求封装成对象,从而让你可以使用不同的请求、队列和日志请求,支持可撤销操作。
优点:
- 支持命令的撤销、重做操作。
- 可以将请求发送者与接收者解耦。
缺点:
- 需要为每个请求创建命令类,增加了类的数量。
- 增加了系统的复杂性。
常见应用场景:
- GUI界面的按钮点击、遥控器控制系统、事务管理。
3. 解释器模式(Interpreter Pattern)
作用:
给定一个语言,定义它的文法,并提供一个解释器来解释句子。
优点:
- 提供了一种易于扩展的方式来处理复杂的语言规则。
- 易于将新规则添加到现有的解释器中。
缺点:
- 如果文法复杂,解释器会变得非常复杂。
- 解析性能可能成为瓶颈。
常见应用场景:
- SQL解析器、正则表达式引擎、编程语言解释器。
4. 迭代器模式(Iterator Pattern)
作用:
提供一种方法来顺序访问集合中的元素,而不暴露集合的内部表示。
优点:
- 可以统一访问集合元素的方式。
- 使得集合类内部的实现细节对外部透明。
缺点:
- 如果集合是动态变化的,迭代器可能会失效。
- 需要为每种集合类实现相应的迭代器。
常见应用场景:
- 集合类的遍历(如ArrayList、LinkedList的迭代器)。
5. 中介者模式(Mediator Pattern)
作用:
通过定义一个中介者对象来封装一组对象的交互,使得对象不需要直接交互。
优点:
- 减少了对象之间的耦合。
- 可以集中控制对象间的交互行为。
缺点:
- 中介者可能会变得非常复杂。
- 所有的交互都通过中介者进行,可能导致中介者成为系统的瓶颈。
常见应用场景:
- 聊天系统、GUI控件的事件处理。
6. 备忘录模式(Memento Pattern)
作用:
在不暴露对象的实现细节的情况下,捕获对象的内部状态,并在之后恢复该状态。
优点:
- 支持对象状态的恢复(撤销操作)。
- 可以在对象外部保存状态,便于管理。
缺点:
- 需要大量的存储空间来保存备忘录。
- 可能会导致程序设计的复杂度增加。
常见应用场景:
- 游戏存档、撤销操作、历史记录管理。
7. 观察者模式(Observer Pattern)
作用:
当一个对象状态发生改变时,所有依赖于它的对象都会自动得到通知并更新。
优点:
- 观察者和主题对象之间解耦。
- 可以动态地增加或删除观察者。
缺点:
- 如果有大量的观察者,可能会导致性能问题。
- 可能会导致不必要的多次更新。
常见应用场景:
- GUI界面的事件处理、消息发布/订阅系统。
8. 状态模式(State Pattern)
作用:
允许对象在内部状态改变时改变其行为,表现得像是改变了一个类。
优点:
- 可以清晰地表示对象的状态变化,避免使用多重条件语句。
- 增强了系统的可维护性。
缺点:
- 增加了类的数量。
- 状态转换需要小心管理。
常见应用场景:
- 状态机(如订单、游戏角色的状态管理)。
9. 策略模式(Strategy Pattern)
作用:
定义一系列算法,并将每个算法封装起来,使得它们可以互换。
优点:
- 可以在运行时切换算法。
- 避免了多个条件语句和复杂的算法逻辑。
缺点:
- 需要创建多个策略类。
- 客户端需要知道策略的选择和管理。
常见应用场景:
- 排序算法的选择、支付方式选择(如支付宝、微信支付)。
10. 模板方法模式(Template Method Pattern)
作用:
定义一个算法的骨架,并允许子类实现算法的某些步骤。
优点:
- 可以在不改变算法结构的情况下,提供灵活的定制点。
- 提高了代码的可复用性。
缺点:
- 过多的模板方法可能导致代码过于复杂。
- 子类需要根据父类的规定方法进行实现。
常见应用场景:
- 游戏框架中的初始化和更新方法,文件处理。
11. 访问者模式(Visitor Pattern)
作用:
允许你在不改变元素类的前提下,定义作用于元素的操作。
优点:
- 可以很容易地为不同的元素对象增加新操作。
- 让操作和数据结构分离,便于扩展。
缺点:
- 增加了访问者类的复杂度。
- 如果元素类层次结构发生改变,访问者类也需要做相应的修改。
常见应用场景:
- 复合元素的结构操作、编译器的语法分析。
以上是当下常用的23种设计模式,后面有时间会把其中常用的几种展开聊一聊。
如有错误欢迎指正交流。
公众号:Altma