常用的设计模式解读
本篇文章旨在作为导读,快速地带领您了解设计模式的基本概念、模式分类以及适用范围。在接下来的章节中,我们将会详细探讨每一种分类的设计模式,包括对每种设计模式的核心原理、适用范围以及实际案例的深入解析。
一、什么是设计模式
设计模式是在软件开发领域中,对常见问题的解决方案的经验总结和最佳实践的抽象表示。它们是在实际开发过程中从反复出现的情况中提炼出来的,旨在帮助开发人员更有效地解决类似问题,并提供可重用的、经过验证的解决方案。
设计模式并不是具体的代码片段,而是关于如何构建软件组件、类、对象之间的关系以及如何应对特定问题的一种通用指导。它们可以看作是一种设计思想,用来指导代码的组织结构,以使代码更易于理解、扩展和维护。
每个设计模式都有一定的命名、结构和特定的上下文,以便在适当的时候使用。设计模式可以分为不同的类型,如创建型模式、结构型模式和行为型模式,每种类型都解决不同类型的问题。
设计模式的核心目标包括:
- 重用性(Reusability):提供经过验证的解决方案,可以在不同的情境中重复使用。
- 可维护性(Maintainability):帮助构建易于理解和修改的代码结构,降低维护成本。
- 扩展性(Extensibility):允许在不影响现有代码的情况下添加新功能或进行修改。
- 灵活性(Flexibility):提供灵活的架构,使系统能够适应变化和需求的变化。
二、设计模式的六大原则
1、开闭原则(Open-Closed Principle,OCP)
开闭原则(Open-Closed Principle,OCP)是面向对象设计的一个基本原则,它强调软件实体(类、模块、函数等)应该对扩展是开放的,但对修改是封闭的。换句话说,当需求变化时,应该通过扩展现有代码来添加新的功能,而不是修改已有的代码。
这个原则的核心思想是,不应该直接修改已有的代码,因为这可能会引入新的错误或破坏现有的功能。相反,应该通过扩展现有代码,通过添加新的代码来引入新的功能,同时保留原有的稳定功能。这种方式可以保持系统的稳定性,同时还能够适应未来的变化。
开闭原则的关键要点包括:
- 开放性(Open for Extension):系统应该允许添加新的功能,而不需要修改现有的代码。这可以通过抽象和接口来实现。
- 封闭性(Closed for Modification):一旦一个类或模块的代码完成并通过测试,就不应该随意修改。这样可以避免不必要的风险。
开闭原则的实现方法包括:
- 抽象和接口:通过定义抽象的接口,允许新的具体实现来扩展功能。客户端代码可以依赖于抽象接口,而不是具体实现。
- 使用继承:通过派生新的子类来扩展功能,但要确保子类能够完全替代父类(遵循里氏替换原则)。
- 使用组合和聚合:将现有的类作为成员变量组合到新的类中,从而扩展功能。
开闭原则的好处包括:
- 提高代码的可维护性:由于不需要频繁修改现有的代码,系统更容易维护。
- 提高代码的稳定性:不会因为修改而引入新的错误。
- 提高代码的扩展性:可以方便地添加新的功能,而不会影响到已有的功能。
2、单一职责原则(Single Responsibility Principle,SRP)
单一职责原则(Single Responsibility Principle,SRP)是面向对象设计的一个基本原则,它建议一个类应该只有一个引起它变化的原因。换句话说,一个类应该只负责一项职责或功能。
这个原则的核心思想是,一个类应该具有高内聚性(Cohesion),即类的各个成员方法和属性应该紧密相关,都围绕着实现一个单一的目标或职责。如果一个类承担了过多的职责,那么它的变化可能会导致其他职责受到影响,从而增加了代码的复杂性和维护成本。
单一职责原则的关键要点包括:
- 职责的界定:一个类应该通过清晰明确的方式定义其职责。如果在设计阶段难以定义类的职责,可能需要将其拆分成多个小类。
- 避免“胖”类:避免将过多的功能集中在一个类中,而应该将不同的职责分布到不同的类中。
- 高内聚性:一个类的成员方法和属性应该紧密相关,围绕一个共同的目标进行设计。
单一职责原则的好处包括:
- 提高代码的可读性:每个类只负责一项职责,使代码更加清晰明了。
- 提高代码的可维护性:职责分离使得修改一个功能不会影响其他功能,减少了引入错误的风险。
- 提高代码的复用性:将不同的职责拆分成独立的类,可以更容易地重用这些类。
单一职责原则的实践方法包括:
- 拆分大类:如果一个类承担了过多的职责,可以将其拆分成多个小类,每个类负责一个独立的职责。
- 引入中间层:如果一个类既要负责业务逻辑又要处理持久化等操作,可以引入一个中间层,将这些职责分离开来。
3、里氏替换原则(Liskov Substitution Principle,LSP)
里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计的一个基本原则,提出派生类应该能够完全替代其基类,并且不会对程序的正确性产生负面影响。也就是说,在使用基类的地方,可以无需修改代码就可以使用派生类来替代。
这个原则强调子类应该遵循基类的行为和约定,确保子类的行为和基类一致,同时可以通过扩展来添加新的功能。如果子类违背了基类的约定,那么可能会导致程序在不同的地方出现不一致的行为,增加代码的错误风险。
里氏替换原则的关键要点包括:
- 子类行为一致性:子类应该复用基类的行为,并且不应该在原有的基础上改变行为。如果需要修改行为,应该通过扩展来添加新的方法。
- 不引入新的前置条件:子类的前置条件(即输入条件)不应该比基类更严格,否则会破坏使用基类的地方。
- 不削弱后置条件:子类的后置条件(即输出结果)不应该比基类更弱,否则会破坏使用基类的地方。
- 不违背基类的约定:子类应该遵循基类的约定,不应该重写基类的方法以违背基类的预期行为。
里氏替换原则的好处包括:
- 提高代码的可扩展性:可以通过添加新的派生类来扩展功能,而不需要修改已有的代码。
- 提高代码的可维护性:子类的行为和基类一致,减少了引入错误的风险。
- 提高代码的可读性:基类的行为和约定可以在多个地方共享,使代码更加一致和易于理解。
里氏替换原则的实践方法包括:
- 重点关注约定:在设计派生类时,重点关注保持基类的行为和约定,不要违背或改变基类的预期行为。
- 使用接口:使用接口来定义基类的约定,从而确保派生类遵循这些约定。
4、依赖倒置原则(Dependency Inversion Principle,DIP)
依赖倒置原则(Dependency Inversion Principle,DIP)是面向对象设计的一个基本原则,提出高层模块不应该依赖于低层模块,而是应该依赖于抽象。同时,抽象不应该依赖于具体,具体应该依赖于抽象。这有助于解耦并提高系统的可维护性。
依赖倒置原则强调:
- 高层模块不应该直接依赖于低层模块:高层模块和低层模块都应该依赖于抽象,而不是相反。这意味着高层模块不应该直接依赖于低层模块的具体实现细节,而是通过抽象接口来交互。
- 抽象不应该依赖于具体:抽象接口应该稳定并且不会随着具体实现的变化而变化。具体的实现应该适应抽象的要求,而不是反过来。
这个原则的关键思想是,通过引入抽象接口,可以将高层模块与低层模块解耦,从而使得系统更加灵活和可维护。这也有助于实现开闭原则,即通过扩展抽象来添加新的功能,而不需要修改已有的代码。
依赖倒置原则的实践方法包括:
- 使用接口或抽象类:在高层模块和低层模块之间定义接口或抽象类,高层模块只依赖于这些抽象,而不直接依赖于具体实现。
- 依赖注入:通过依赖注入(Dependency Injection,DI)将具体实现的依赖传递给高层模块,以确保高层模块不需要关心具体实现的细节。
依赖倒置原则的好处包括:
- 解耦高层模块和低层模块,降低模块之间的耦合度。
- 提高代码的可维护性,因为更改低层模块的实现不会影响高层模块。
- 提高代码的可测试性,可以通过模拟抽象接口来测试高层模块的功能。
5、接口隔离原则(Interface Segregation Principle,ISP)
接口隔离原则(Interface Segregation Principle,ISP)是面向对象设计的一个基本原则,强调客户端不应该被迫依赖于它们不使用的接口。简而言之,一个类不应该强制实现它用不到的接口方法。
这个原则的核心思想是,接口应该是小而专一的,而不是大而臃肿的。一个类应该只需要实现它所需要的那部分接口,而不必强制实现多余的接口方法。这样可以避免不相关的接口方法对类的影响,降低耦合度。
接口隔离原则的关键要点包括:
- 定义精简的接口:接口应该只包含实现相关功能所需的方法,避免定义大而全的接口。
- 避免"胖"接口:避免在一个接口中定义过多的方法,使得不同的类需要实现不相关的方法。
- 客户端不受影响:一个类的客户端不应该因为接口的变化而受到影响,即客户端只应该依赖于它们实际使用的方法。
接口隔离原则的好处包括:
- 提高代码的灵活性:接口的小而专一性使得系统更加灵活,能够更容易地进行扩展和修改。
- 提高代码的可维护性:接口的精简性减少了不相关方法的影响,使得维护变得更加容易。
- 提高代码的可读性:接口的精简性使得代码更易于理解,减少了不必要的方法干扰。
接口隔离原则的实践方法包括:
- 拆分大接口:将大接口拆分成多个小接口,每个接口关注一个特定的功能。
- 使用接口继承:通过接口继承来定义多个小接口,从而保持接口的精简性。
6、合成/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)
合成/聚合复用原则(Composite/Aggregation Reuse Principle,CARP)是面向对象设计的一个原则,提倡优先使用对象组合(合成)和聚合,而不是继承来实现代码复用。这个原则强调通过对象之间的组合关系,来实现代码的复用和组织,而不是通过继承的方式。
这个原则的关键思想是,通过对象组合或聚合的方式,可以在不破坏现有类的结构的情况下,将多个类组合起来创建新的类。这有助于构建更加灵活和可扩展的系统,避免了继承带来的限制和问题。
合成/聚合复用原则的要点包括:
- 优先使用组合:在设计时,优先考虑将多个类组合起来,而不是通过继承来实现复用。
- 避免“白箱”复用:通过继承实现复用会暴露基类的内部实现细节,称为“白箱”复用。而通过组合可以实现“黑箱”复用,隐藏内部实现细节。
- 灵活性和可扩展性:组合关系可以在运行时动态地更改,而继承关系在编译时已经确定。
合成/聚合复用原则的好处包括:
- 提高代码的灵活性:通过组合可以更灵活地组织和复用类,避免了继承的静态关系。
- 提高代码的可扩展性:通过组合可以动态地添加、移除或替换组件,从而实现更加可扩展的系统。
合成/聚合复用原则的实践方法包括:
- 使用对象组合:通过将多个类实例作为组件组合到一个新的类中,创建复合对象。
- 使用聚合关系:将一个类作为成员变量嵌入到另一个类中,实现聚合关系。
三、设计模式类别
1、创建型模式(Creational Patterns)
这些模式关注对象的实例化过程,帮助在对象的创建中更加灵活和可控。
-
工厂模式(Factory Pattern)
工厂模式它提供了一种通用的方式来创建对象,而不需要在客户端代码中直接实例化具体的类。工厂模式的主要思想是将对象的创建封装在一个工厂类中,客户端代码通过调用工厂方法来创建对象,从而实现了对象的创建和使用的分离。
-
抽象工厂模式(Abstract Factory Pattern)
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而不需要指定它们的具体类。
-
单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
-
原型模式(Prototype Pattern)
原型模式通过复制现有对象来创建新的对象,从而避免了直接实例化。这种方式对于创建开销较大的对象特别有用。
-
建造者模式(Builder Pattern)
建造者模式将一个复杂对象的构建过程与其表示分离,使得相同的构建过程可以创建不同的表示。
详情请跳转:https://blog.csdn.net/weixin_47772522/article/details/132407855
2、结构型模式(Structural Patterns)
这些模式关注类和对象的组合,以构建更大的结构。
-
适配器模式(Adapter Pattern)
适配器模式允许将不同接口的类协同工作。它通过创建一个中间层来转换一个类的接口成另一个客户端所期望的接口。
-
桥接模式(Bridge Pattern)
桥接模式将抽象部分和实现部分分离,以允许它们可以独立变化。它将一个复杂的类层次结构分成两个独立的维度。
-
组合模式(Composite Pattern)
组合模式允许将对象组合成树状结构,以表示“整体-部分”的层次关系。客户端可以像处理单个对象一样处理组合对象。
-
装饰器模式(Decorator Pattern)
装饰器模式允许在不修改现有对象结构的情况下,动态地添加新功能或修改行为。
-
外观模式(Facade Pattern)
外观模式提供了一个统一的接口,以简化复杂子系统的使用。它隐藏了子系统的复杂性,提供一个更高级别的接口供客户端使用。
-
享元模式(Flyweight Pattern)
享元模式用于减少大量相似对象的内存占用。它共享相似对象之间的公共部分,以节省内存和资源。
-
代理模式(Proxy Pattern)
代理模式用于控制对对象的访问。它提供了一个代理对象,允许在访问对象之前或之后添加一些额外的操作。
详情请跳转:https://blog.csdn.net/weixin_47772522/article/details/132408215
3、行为型模式(Behavioral Patterns)
这些模式关注对象之间的通信和责任分配,以实现更灵活的交互。
-
责任链模式(Chain of Responsibility Pattern)
责任链模式建立一个对象链来处理请求。当一个请求进入链中,每个对象依次检查并处理该请求,或将其传递给下一个对象。
-
命令模式(Command Pattern)
命令模式将请求或操作封装成对象,以允许参数化客户端对象,并支持撤销操作。
-
解释器模式(Interpreter Pattern)
解释器模式用于定义语言的文法规则,以及如何解释和执行该语言中的表达式。
-
迭代器模式(Iterator Pattern)
迭代器模式提供一种顺序访问集合对象元素的方式,而无需暴露其内部结构。
-
中介者模式(Mediator Pattern)
中介者模式用于减少对象之间的直接通信,通过引入中介者对象来协调和管理对象之间的交互。
-
备忘录模式(Memento Pattern)
备忘录模式用于捕获一个对象的内部状态,并允许在以后恢复到该状态。
-
观察者模式(Observer Pattern)
观察者模式定义了对象之间的一种一对多依赖关系,当一个对象的状态发生改变时,其所有依赖的对象都会得到通知并更新。
-
状态模式(State Pattern)
状态模式允许对象在内部状态发生改变时改变其行为,使对象看起来好像在运行时更改了类。
-
策略模式(Strategy Pattern)
策略模式定义一系列算法,将每个算法封装成一个对象,使得它们可以互换使用。
-
模板模式(Template Method Pattern)
模板模式定义了一个算法的骨架,将一些步骤的实现延迟到子类中。
-
访问者模式(Visitor Pattern)
访问者模式允许在不改变对象结构的情况下,为对象结构中的每个元素添加新的操作。
详情请跳转:https://blog.csdn.net/weixin_47772522/article/details/132408362
四、参考文档
设计模式demo项目源码地址:https://gitee.com/greedy-little-squirrel/design-patterns