文章目录
为什么学习设计模式?
设计模式是解决特定软件设计问题的通用、可重用的解决方案。它们不仅能帮助你编写更清晰、更灵活的代码,还能提升代码的可维护性和扩展性。通过学习设计模式,你可以:
- 提高代码质量: 设计模式提供了一种结构化的方法来解决常见的编程问题,从而使代码更易于理解和维护。
- 促进团队协作: 熟悉设计模式的开发者可以更高效地进行团队合作,因为他们使用的是公认的最佳实践。
- 增强问题解决能力: 设计模式是无数优秀程序员智慧的结晶,学习它们可以帮助你更快地找到问题的解决方案。
六大设计原则
设计模式是解决特定软件设计问题的通用、可重用的解决方案。为了确保这些模式能够有效地解决设计问题并提高代码质量,它们通常遵循六大设计原则,即SOLID原则和迪米特法则:
1. 单一职责原则 (Single Responsibility Principle, SRP)
定义: 一个类应该只有一个引起它变化的原因。
意义: 确保每个类只负责一项职责,这样当需求变化时,只会有一个类需要修改,从而提高代码的稳定性和可维护性。
应用场景: 将功能分离到不同的类中,避免类过于庞大和复杂。
2. 开闭原则 (Open/Closed Principle, OCP)
定义: 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
意义: 允许在不修改现有代码的基础上扩展功能,从而减少引入新错误的风险。
应用场景: 通过接口和抽象类来定义基础功能,允许具体的实现类进行扩展。
3. 里氏替换原则 (Liskov Substitution Principle, LSP)
定义: 子类应该可以替换父类并且不会影响程序的正确性。
意义: 确保继承关系的正确性,子类可以扩展父类的功能而不破坏父类的行为。
应用场景: 在继承关系中,子类必须完全实现父类的抽象方法,并且不能违反父类的行为规范。
4. 接口隔离原则 (Interface Segregation Principle, ISP)
定义: 客户端不应该依赖它不需要的接口。
意义: 避免接口过于庞大和臃肿,减少不必要的依赖和耦合。
应用场景: 将大接口拆分为多个小接口,客户端只需实现自己需要的接口。
5. 依赖倒置原则 (Dependency Inversion Principle, DIP)
定义: 高层模块不应该依赖低层模块,两者都应该依赖抽象;抽象不应该依赖细节,细节应该依赖抽象。
意义: 通过抽象来解耦高层模块和低层模块,提高系统的灵活性和可维护性。
应用场景: 使用接口和抽象类来定义依赖关系,而不是直接依赖具体的实现类。
6. 迪米特法则 (Law of Demeter, LoD) 或 最少知识原则 (Least Knowledge Principle, LKP)
定义: 一个对象应该对其他对象有尽可能少的了解。
意义: 减少对象之间的耦合,使得系统更具弹性和可维护性。
应用场景: 每个对象只与直接的朋友通信,避免与间接的朋友通信。
设计模式分类
一、创建型模式
这类模式关注于对象的创建机制,帮助系统独立于如何创建、组合和表示其对象。
- 单例模式 (Singleton): 确保一个类只有一个实例,并提供一个全局访问点。
- 应用场景
- 数据库连接:确保只有一个数据库连接实例,避免资源浪费
- 日志记录器:全局只使用一个日志记录器实例
- 配置管理:系统中只有一个配置管理实例
- 应用场景
- 工厂模式 (Factory): 定义一个接口用于创建对象,但让子类决定实例化哪个类。
- 应用场景
- 对象创建逻辑复杂:当对象创建设计复杂的初始化逻辑时
- 需要解耦对象创建和使用
- 产品族:同一产品族的不同产品需要创建
- 应用场景
- 抽象工厂模式 (Abstract Factory): 提供一个接口,用于创建相关或依赖对象的家族,而不指定具体的类。
- 应用场景
- 产品族:创建一系列相关或依赖的对象
- 跨平台应用:为不同平台创建不同的产品家族
- 应用场景
- 建造者模式 (Builder): 将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。
- 应用场景
- 复杂对象的创建
- 配置灵活的对象:允许客户端选择性地配置对象的某些部分
- 应用场景
- 原型模式 (Prototype): 通过复制现有对象来创建新对象,而不是通过实例化类。
- 应用场景
- 对象克隆:当创建的成本较高时,可以使用原型模式克隆现有对象
- 创建复杂对象
- 应用场景
二、结构型模式
这类模式关注于类和对象的组合,以形成更大的结构。
- 适配器模式 (Adapter): 将一个类的接口转换成客户端所期望的另一个接口。
- 应用场景
- 接口不兼容:当两个不兼容的接口需要一起工作时
- 遗留系统:将新的代码与旧系统兼容
- 应用场景
- 装饰器模式 (Decorator): 动态地给对象添加职责,而不改变其结构。
- 应用场景
- 动态扩展功能:在不改变现有对象结构的情况下,动态地添加功能
- Java I/O 类:使用装饰器模式扩展 I/O 类的功能
- 应用场景
- 代理模式 (Proxy): 为其他对象提供一个占位符,以控制对这个对象的访问。
- 应用场景
- 远程代理:为一个远程对象提供本地代表
- 虚拟代理:延迟初始化对象知道真正需要使用
- 安全代理:控制对原始对象的访问权限
- 应用场景
- 组合模式 (Composite): 将对象组合成树形结构,以表示“部分-整体”的层次结构。
- 应用场景
- 树形结构:处理具有树形结构的对象,如文件系统、组织结构等
- 统一处理单个对象和对象组合:客户都安可以统一处理单个对象和组合对象
- 应用场景
- 外观模式 (Facade): 为子系统中的一组接口提供一个统一的接口。
- 应用场景
- 简化复杂子系统:为复杂的子系统提供一个简单的接口
- 解耦客户端和子系统: 减少客户端和子系统之间的耦合。
- 应用场景
- 桥接模式 (Bridge): 将抽象部分与实现部分分离,使它们可以独立变化。
- 应用场景
- 抽象和实现分离:将抽象部分与实现部分分离,使得它们可以独立变化
- 多维度变化:当系统需要在多个维度上扩展时,桥接模式可以简化设计
- 避免继承爆炸:使用桥接模式可以避免生成过多的子类
- 应用场景
- 享元模式 (Flyweight): 通过共享来高效地支持大量细粒度的对象。
- 应用场景
- 大量相似对象:当系统中有大量的相似对象时,使用享元模式减少内存占用
- 对象共享:通过共享对象减少系统资源的消耗
- 应用场景
三、行为型模式
这类模式关注于对象之间的通信和职责分配。
- 策略模式 (Strategy): 定义一系列算法,并将每个算法封装起来,使它们可以互换。
- 应用场景
- 算法选择:允许客户端选择不同的算法或策略
- 动态切换算法:在运行时动态地切换算法
- 应用场景
- 观察者模式 (Observer): 定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会收到通知并自动更新。
- 应用场景
- 事件处理系统:在事件驱动系统中,如GUI事件处理
- MVC架构:视图和模型之间使用观察者模式进行通信
- 应用场景
- 命令模式 (Command): 将请求封装为对象,从而使你可以用不同的请求、队列或日志来参数化其他对象。
- 应用场景
- 请求封装:将请求封装为对象,从而允许参数化客户端
- 日志记录和重做:记录和重做操作,如数据库事务管理
- 应用场景
- 迭代器模式 (Iterator): 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部表示。
- 应用场景
- 集合遍历:提供一种统一的遍历集合的方式,隐藏集合的具体实现
- 解耦集合和遍历:使集合和遍历逻辑分离
- 应用场景
- 模板方法模式 (Template Method): 定义一个操作中的算法骨架,而将一些步骤延迟到子类中。
- 应用场景
- 算法框架:定义算法的框架,允许子类实现细节
- 流程控制:定义一系列的执行顺序
- 应用场景
- 状态模式 (State): 允许对象在内部状态改变时改变其行为。
- 应用场景
- 状态机:管理对象的状态切换,如有限状态机
- 复杂状态管理:减少条件语句的使用,使状态管理更清晰
- 应用场景
- 责任链模式 (Chain of Responsibility): 为请求创建一条接收者链,每个接收者都包含对另一个接收者的引用。
- 应用场景
- 请求处理链:允许一个请求在多个处理对象之间传递,直到被处理
- 日志记录:不同级别的日志记录器处理不同的日志请求
- 应用场景
- 解释器模式 (Interpreter): 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
- 应用场景
- 简单语言解释:如正则表达式、SQL解析等
- 规则引擎:解释和执行规则引擎中的规则
- 应用场景
- 中介者模式 (Mediator): 用一个中介对象来封装一系列对象交互,从而使这些对象不需要显式地相互引用。
- 应用场景
- 对象间复杂的通信:当系统中对象之间的通信复杂且相互依赖时,使用中介者模式可以减少耦合
- 多人协作:如多人聊天室,每个参与者通过中介者进行通信
- 应用场景
- 备忘录模式 (Memento): 在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便该对象可以恢复到此状态。
- 状态保存和恢复:允许在不破坏封装性的前提下保存和恢复对象的状态
- 撤销操作:在应用程序中实现撤销功能
- 访问者模式 (Visitor): 表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
- 应用场景
- 操作集合:对集合中的每个元素执行某种操作
- 数据结构稳定但操作多变:数据结构稳定,但需要频繁添加新操作
- 应用场景