一、理解什么是设计模式
设计模式是对软件设计开发过程中反复出现的某类问题的通用解决方案。
设计模式是一个在软件设计领域中被广泛应用的概念,它指的是一套被公认为有效的解决特定问题的设计思路和方法。
设计模式更多的是指导思想和方法论,而不是现成的代码,当然每种设计模式都有每种语言中的具体实现方式。
学习设计模式更多的是理解各种模式的内在思想和解决的问题,毕竟这是前人无数经验总结成的最佳实践,而代码实现则是对加深理解的辅助。
设计模式的目的是帮助开发人员在面对复杂的软件设计问题时,可以更加高效、优雅地解决问题,并提高软件的可重用性、可维护性、可扩展性和可靠性。
设计模式通常包括一系列定义良好的类和对象,以及它们之间的关系和交互方式,是一种被反复使用、实践并广泛认可的模式或范例。
二、常见的设计模式有以下几种
序号 | 分类 | 名称 | 理解 |
1 | 创建型模式 | 单例模式 | 单例模式是一种创建型设计模式,它保证了一个类只有一个实例,并提供了一个全局的访问点。单例模式常常用于管理全局唯一的资源,如全局配置、日志记录、数据库连接等。 单例模式是一种具体的实现,而不是一种抽象的概念模式。在实现单例模式时,需要考虑线程安全、并发控制、延迟实例化等问题。 在单例模式的实现中,常用的方法有:
|
2 | 工程模式 | 工程模式是一种软件设计模式,它旨在提供一种可重用的解决方案,以解决在软件开发中常见的设计问题。工程模式强调了软件设计的结构透明性和模块化,使得软件系统更易于维护和扩展。工程模式通常包括以下几个部分:
通过使用工程模式,可以将具体的实现细节与客户端代码分离开来,从而实现松耦合的设计。这样一来,当需求发生变化时,只需修改具体类的实现即可,而不会影响到客户端代码。工程模式在大型软件系统中应用广泛,例如,Java语言中的工厂模式、单例模式、抽象工厂模式等都是工程模式的典型应用。 |
|
3 | 抽象工厂模式 | 抽象工厂模式是一种创建型设计模式。它允许客户端通过接口创建一组相关或相互依赖的对象,而不需要指定它们的具体类。抽象工厂模式与工厂方法模式的区别在于,它可以创建多个产品族,而不仅仅是一个产品等级结构。在抽象工厂模式中,客户端不需要创建对象,而是通过工厂接口来获取所需的对象实例。这样可以提高系统的灵活性和可扩展性,因为客户端只需要知道工厂接口,而无需关心具体的产品实现细节。 | |
4 | 建造者模式 | 建造者模式是一种创建型设计模式,它允许你创建复杂对象的过程与其表示相分离。这样,同样的构建过程可以创建不同的表示形式。建造者模式使得你可以在不同的步骤中创建对象,这些对象在创建时可能需要不同的步骤,并且可能需要以不同的顺序执行这些步骤。 在建造者模式中,有一个“Director”负责控制对象的构建过程,而有一个“Builder”负责实际构建对象。通过这种方式,Director 和 Builder 可以相互独立,从而使得构建过程更加灵活和可扩展。 建造者模式通常被用于创建对象的构建过程比较复杂,或者需要多个步骤才能完成的情况。它可以让你更好地组织代码,并且提高代码的复用性和可维护性。 |
|
5 | 原型模式 | 原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新对象,而不是通过实例化新对象并初始化它们的值。即原型模式是一种通过克隆来创建对象的模式。 在原型模式中,一个原型对象被克隆来创建新的对象。这个原型对象是一个已经初始化的对象,它包含了我们需要的所有属性和方法。新对象可以通过复制这个原型对象来创建,并且可以修改其属性值和方法实现以满足特定的需求。 原型模式被广泛应用于创建大量相似对象的场景,以避免重复的初始化过程,并提高对象创建的效率。 |
|
6 | 结构型模式 | 适配器模式 | 适配器模式是一种结构型设计模式,它允许将不兼容的接口转换为客户端所期望的接口。它解决了不同类之间接口不兼容的问题,使得这些类可以协同工作。 适配器模式有三个角色:
适配器模式主要通过适配器类来实现对现有接口的转换,使得客户端代码可以透明地使用新的接口。适配器模式的优点是可以提高代码重用性和灵活性,同时降低了系统的耦合度。但是,适配器模式也会增加系统的复杂度,因为需要增加适配器类来进行接口转换。 |
7 | 桥接模式 | 桥接模式是一种结构型设计模式,用于将抽象与实现分离,使它们可以独立地变化。桥接模式的核心思想是把实现和抽象分离开来,让它们可以独立变化,从而达到更好的灵活性。 在桥接模式中,抽象部分定义了一个接口,它包含了对某种功能的抽象方法,而实现部分则是提供了这个功能的具体实现。两者之间通过一个桥接接口进行通信。 桥接模式的优点在于它能够轻松地扩展和修改系统的功能,同时减少了不必要的类之间的依赖关系,从而提高了系统的灵活性和可维护性。 桥接模式的缺点在于它会增加系统的复杂性和代码量,需要设计者有较强的抽象能力和对系统的整体架构有清晰的把握。 |
|
8 | 组合模式 | 组合模式是一种结构型设计模式,它将对象组合成树状结构,以表示“部分-整体”的层次结构。组合模式让客户端可以统一对待单个对象和组合对象,从而简化了客户端的代码,同时也使得整个系统更加灵活和可扩展。 在组合模式中,树状结构中的每个节点都可以是单个对象或者是一个组合对象。每个组合对象都可以包含其它组合对象和单个对象,从而形成了一个树形结构。所有的节点都实现了相同的接口,这样客户端可以使用相同的方式来访问树中的所有节点。 组合模式的优点在于它可以简化客户端的代码,因为客户端不需要关注是单个对象还是组合对象,而只需要使用相同的方式来操作它们。另外,组合模式也支持嵌套的结构,因此可以表示非常复杂的层次结构。 组合模式的缺点在于它可能会加重系统的复杂性,因为需要为每个节点都实现相同的接口。此外,组合模式也可能会带来一些性能上的问题,因为在处理大型树形结构时,可能需要遍历整个树形结构,从而影响系统的性能。 |
|
9 | 装饰模式 | 装饰模式是一种结构型设计模式,它允许在运行时动态地将责任附加到对象上,同时不改变其行为和接口。 装饰模式通过将一个对象嵌套在另一个对象中来实现这一点,将对象进行逐层的封装。这样可以在不影响原有代码的情况下,动态地为对象添加新的行为;同时也可以动态地移除已有的行为。这样做的好处是,可以避免使用继承来扩展对象的行为,降低了代码的复杂性,同时提供了更大的灵活性和可维护性。 装饰模式的核心思想是将对象的行为分离成单独的类,然后将这些类动态地组合在一起。这使得我们可以更加灵活地管理对象的不同行为,并且可以根据需要添加、删除或替换这些行为。 |
|
10 | 外观模式 | 外观模式是一种结构型设计模式,它提供了一个高层次的接口,以简化一组复杂的子系统的使用。它允许客户端通过简化的接口来与复杂的系统进行通信,而不需要了解系统的详细实现。 外观模式包含一个外观类,该类包含对子系统中各个类的引用。客户端通过外观类与子系统进行通信,而不需要直接调用子系统中的类。外观类将客户端的请求转发给子系统中的适当类,并将结果返回给客户端。 外观模式的优点包括: 1. 简化了客户端与子系统之间的交互,使得代码更加简洁易懂。 2. 降低了客户端与子系统之间的耦合度,提高了系统的可维护性和可扩展性。 3. 提供了一个安全的、受控的访问方式,以防止客户端直接访问子系统中的类。 4. 提高了系统的性能,因为外观类可以缓存请求结果,减少了对子系统的访问次数。 外观模式常用于大型系统中,可以帮助客户端更好地组织代码,并提高代码重用性和可维护性。 |
|
11 | 享元模式 | 享元模式是一种结构型设计模式。它的主要目的是减少程序所需的内存和计算资源,通过共享对象的方法来有效地支持大量的小型对象。这个模式将对象分为两个部分:内部状态和外部状态。内部状态是可以共享的,而外部状态则是每个对象都不同的。 在享元模式中,共享对象通常是不可变的,一旦创建,就不能修改它们的内部状态,因为修改它们的状态可能会影响其他使用该对象的客户端。 享元模式常用于需要大量相似对象的场景,例如文本编辑器中的字体和颜色,图像编辑器中的像素颜色等。 这个模式的优点是可以减少内存使用,提高程序性能,缺点是代码变得更加复杂,需要仔细维护共享对象的状态。 |
|
12 | 代理模式 | 代理模式是一种常见的设计模式,它允许通过代理对象来控制对某个对象的访问。代理对象与被代理对象实现相同的接口,因此客户端无法区分其是否在与被代理对象直接交互。 代理模式的使用场景主要有以下几种: 1. 远程代理:通过代理对象访问远程服务器上的对象,可以在客户端和服务器之间建立网络连接。 2. 虚拟代理:代理对象在访问被代理对象之前执行一些操作,例如创建被代理对象需要的资源,只有在必要时才会实例化被代理对象。 3. 安全代理:代理对象对访问进行控制,例如验证客户端是否有访问权限。 4. 智能代理:代理对象可以在被代理对象上添加一些额外的操作,例如缓存结果、记录日志等。 优点: 1. 代理对象可以隐藏被代理对象的具体实现,保护其知识产权和商业秘密。 2. 代理对象可以在访问被代理对象前进行验证和授权,提高系统的安全性。 3. 代理对象可以在访问被代理对象时进行一些额外的操作,例如缓存结果、记录日志等。 缺点: 1. 代理模式会增加系统的复杂度,需要额外的代码来实现代理对象和被代理对象之间的交互。 2. 代理对象的性能可能会受到影响,特别是在执行一些额外操作时。 |
|
13 | 行为型模式 | 责任链模式 | 责任链模式是一种行为设计模式,在该模式中,多个对象组成一个链,并依次尝试处理请求,直到其中一个对象能够处理该请求为止。每个对象都有机会处理请求或者将其传递给链中的下一个对象,这样的设计能够避免请求发送者和接收者之间的直接耦合。责任链模式的主要目的是将请求从一个对象传递到另一个对象,直到找到能够处理该请求的对象为止,这样能够提高代码的灵活性并降低系统的复杂度。其中的链可以由任何对象组成,例如对象、方法、组件等。 |
14 | 命令模式 | 命令模式(Command Pattern)是一种行为型模式,它将请求封装成对象,从而允许使用不同的请求、队列或者日志来参数化其他对象。该模式允许将请求发出者和请求接收者解耦。 在命令模式中,请求作为命令封装在对象中,并像其他对象一样进行处理。命令对象可以使用不同的请求和参数化它们,以便将需要执行的命令发送给不同的对象。命令模式的核心在于命令对象的抽象性,以及将请求的发送者和接收者分离的能力。 命令模式的参与者包括: - Command:命令接口,定义执行操作的统一接口。 - ConcreteCommand:具体命令实现类,实现执行操作接口并保存接收者对象引用,可通过接收者对象完成请求的操作。 - Invoker:命令的发送者,负责发送命令并以一定的方式接受命令执行结果。 - Receiver:请求的接收者,定义请求执行具体操作的类。 命令模式常见的应用场景包括:多级撤销操作、队列请求等。 |
|
15 | 解释器模式 | 解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言的文法,并且定义了一个解释器,用于解释该语言中的语句。这种模式将某个特定的领域问题分解为一组语法规则,然后用这些规则来解释执行问题。 解释器模式中的核心是解释器,它负责解释语言中的句子,并执行相应的操作。解释器通常由一个抽象语法树来表示语言的结构,每个节点表示一个语言元素,并且包含了具体的执行动作。 解释器模式适用于需要频繁进行某些特定操作的场景,比如一些领域特定语言或者一些规则引擎等。它可以将复杂的语言结构简化为一些简单的语法规则,并且可以方便地添加新的规则和操作。但是,由于解释器需要解释每个语言元素,因此解释器模式可能会影响性能和效率。 |
|
16 | 迭代器模式 | 迭代器模式是一种行为型设计模式,它允许访问一个聚合对象的各个元素,而又不暴露该对象的内部结构。换句话说,迭代器模式提供了一种方法来访问容器对象中的所有元素,而无需暴露其实现细节。迭代器模式分离了对象的遍历行为,使得代码更加灵活,可维护性也更好。 在迭代器模式中,我们通常会定义一个迭代器接口,该接口包含了访问聚合对象中各个元素的方法。聚合对象则负责实现该接口,并提供访问自身元素的方法。客户端代码会使用迭代器接口来遍历聚合对象中的元素,而不关心具体实现细节。 迭代器模式能够很好地应对容器对象结构的变化,因为只需修改聚合对象的实现即可,而不会影响到客户端代码。此外,迭代器模式能够使代码更加简洁,因为遍历容器对象的代码不再分散在客户端代码中。 |
|
17 | 中介者模式 | 中介者模式(Mediator Pattern)是一种行为型设计模式,它允许多个对象之间通过一个中介对象来协调和通信,从而降低对象之间的耦合度。中介者模式的核心思想是将对象之间的通信和协作抽象出来,通过中介者来处理这些通信和协作,使得对象之间只需要与中介者交互,而不需要直接交互。这样可以减少对象之间的依赖关系,从而实现更松耦合的设计。 中介者模式通常包括一个中介者(Mediator)和多个同事对象(Colleague)。同事对象之间需要通信时,不是直接相互引用,而是通过中介者来实现消息的转发和协调。中介者负责协调和管理同事对象之间的关系和行为,从而实现解耦的效果。中介者模式常用于复杂的应用场景中,如图形界面设计、服务器端程序设计等。 中介者模式的优点包括: 1. 降低了对象之间的耦合性,使得对象之间的交互更加灵活和可扩展; 2. 简化了对象之间的通信和协作,将复杂的逻辑集中在中介者中,更容易理解和维护; 3. 规范了对象之间的交互方式,在代码中体现了设计的整体思想和理念,可读性和可维护性更好。 中介者模式的缺点包括: 1. 中介者对象可能会变得过于复杂和庞大,难以维护; 2. 中介者模式增加了系统的复杂度,可能会降低系统的性能; 3. 中介者模式需要对系统的整体设计有一定的把握和理解,需要投入较高的开发成本。 |
|
18 | 备忘录模式 | 备忘录模式(Memento Pattern)是一种行为设计模式,它允许你在不暴露对象实现细节的情况下保存和恢复其内部状态。该模式涉及三种角色:发起人、备忘录和管理者。 发起人是需要被保存状态的对象。备忘录是一个简单的对象,它存储了发起人的某个状态。管理者负责存储备忘录,并在需要时将其返回给发起人。 使用备忘录模式,你可以随时保存发起人的状态,并在需要时将其还原。这可以让你在不破坏对象封装性的情况下进行恢复。 此外,该模式还支持撤销操作。当你执行一系列操作后,可以将当前状态存储为备忘录。如果之后你想撤销操作,可以恢复到之前的备忘录,达到撤销操作的目的。 备忘录模式在实际开发中常用于实现撤销和恢复功能、快照和版本控制等场景。 |
|
19 | 观察者模式 | 观察者模式是一种行为型设计模式,它允许一个对象(被观察者)在其状态发生变化时自动通知一组依赖它的对象(观察者)。 在观察者模式中,被观察者对象(也称为主题或发布者)维护一组观察者对象,并在其状态发生变化时通知观察者。观察者对象实现了一个接口,该接口允许被观察者对象注册、注销和通知观察者。 观察者模式可以用于许多场景,如事件处理、用户界面、消息传递等。它提供了一种松散耦合的方式来实现对象之间的通信,使得被观察者和观察者可以独立地进行修改和扩展,而不会影响到其他对象。 |
|
20 | 状态模式 | 状态模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变它的行为。该模式将状态封装成不同的类,并将对象在不同状态下的行为委托给表示相应状态的对象来处理。这样可以减少条件语句的嵌套,使代码更加简洁与可维护。 状态模式主要包含以下角色: - Context(上下文):定义客户端所感兴趣的接口,并维护一个当前状态的实例,即维护一个State类型的对象。 - State(抽象状态):定义一个接口,表示一个状态,并封装状态相关的行为。 - ConcreteState(具体状态):实现State接口,处理与某个状态相关的行为。 应用场景: - 对象的行为随着状态的改变而改变; - 有一个有限的状态集合,并且状态之间存在转换关系; - 代码中包含大量的条件语句,可以使用状态模式来替代条件语句,使代码更加简洁与可维护。 |
|
21 | 策略模式 | 策略模式是一种行为设计模式,其目的是允许在运行时根据不同的情况选择不同的算法或行为。该模式将算法封装在单独的策略类中,并使它们互相替换。 使用策略模式的优势包括: 1. 提高代码的可维护性,易于添加或修改策略。 2. 提高代码的可测试性,可以针对不同策略编写独立的测试用例。 3. 降低代码的复杂度,将不同策略的实现分离,使得代码更清晰、更易于理解。 例如,一个电商网站可能需要根据不同的促销活动来计算折扣价格。使用策略模式可以创建不同的策略类来计算不同类型的折扣,例如按照百分比折扣、按照固定折扣、按照满减活动等。然后在运行时根据不同的促销活动选择不同的策略类来计算折扣价格。 |
|
22 | 模板方法模式 | 模板方法模式是一种设计模式,它提供了一种在父类中定义操作中的算法骨架,而将一些步骤延迟到子类中实现的方式。因此,模板方法使得子类可以在不改变算法结构的情况下重新定义某些步骤。 这个模式的主要思想是将一些通用的步骤或算法实现在父类中,并定义一些占位符操作(称为“钩子”),这些占位符操作在子类中实现。然后在父类中使用这些操作来完成算法骨架,并在子类中实现占位符操作,以完成算法的具体实现。 模板方法模式有以下优点: 1. 通过将通用的算法实现放在父类中,可以避免代码重复。 2. 使用模板方法可以保持算法的稳定性,因为算法骨架不会改变。 3. 可以通过在子类中实现占位符操作来定制算法,从而实现代码重用和扩展。 模板方法模式的缺点是,父类和子类之间的关系可能变得复杂,因为子类必须遵守父类定义的算法骨架。如果算法骨架改变,可能需要更改所有子类的实现。 |
|
23 | 访问者模式 | 访问者模式是一种行为型设计模式,它允许在不改变对象结构的情况下定义对该结构中对象的新操作。该模式将算法与其操作的对象分离开来,从而使其更易于添加、更容易维护。 在访问者模式中,有两个重要的角色:访问者和被访问者。被访问者是一个对象结构,例如一个树形结构或一个列表。访问者可以在不改变对象结构的情况下,对这些对象进行操作。 访问者模式的主要优点包括: 1. 使得添加新的操作变得更加简单,只需要增加新的访问者。 2. 将算法与其操作的对象分离开来,使得每个类的功能单一,更易维护。 3. 可以在不改变对象结构的情况下增加新的操作。 访问者模式的主要缺点包括: 1. 需要对对象结构进行重构,使其能够接受访问者访问。 2. 在对象结构稳定并且不经常改变时,访问者模式可能会增加复杂度,因为需要额外的接口和类来实现。 3. 访问者模式可能会影响程序的性能,因为它需要额外的类和接口来实现。 |
此外还有并发模式、架构模式等等。这些设计模式都是经过实践考验的经验总结,可以帮助开发人员快速解决一些常见的问题,在提高代码质量、可读性等方面起到积极的作用。