当前位置: 首页 > news >正文

【后端高频面试题--设计模式下篇】

🚀 作者 :“码上有前”
🚀 文章简介 :后端高频面试题
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬

后端高频面试题--设计模式下篇

  • 往期精彩内容
  • 设计模式总览
  • 模板方法模式
    • 怎么理解模板方法模式
    • 模板方法模式的优缺点
    • 模板方法模式的应用场景
    • 代码实现模板方法模式
  • 外观模式
    • 怎么理解外观模式
    • 外观模式的优缺点
    • 外观模式的应用场景
    • 代码实现外观模式
  • 原型模式
    • 怎么理解原型模式
    • 原型模式的优缺点
    • 原型模式的应用场景
    • 代码实现原型模式
  • 策略模式
    • 怎么理解策略模式
    • 策略模式的优缺点
    • 策略模式的应用场景
    • 代码实现策略模式

往期精彩内容

【后端高频面试题–设计模式上篇】
【后端高频面试题–设计模式下篇】
【后端高频面试题–Linux篇】
【后端高频面试题–Nginx篇】
【后端高频面试题–Mybatis篇】
【后端高频面试题–SpringBoot篇】

设计模式总览

在这里插入图片描述

模板方法模式

怎么理解模板方法模式

模板方法模式是一种行为型设计模式,它定义了一个操作中的算法骨架,将一些步骤延迟到子类中实现。模板方法模式通过模板方法来控制算法的结构,同时允许子类根据需要重写特定步骤,以实现自己的行为。

在模板方法模式中,有两个关键角色:

  1. 模板方法(Template Method):
    模板方法是一个抽象方法或虚拟方法,定义了算法的骨架。它通常是一个具体的方法,其中包含了一系列的步骤或操作,这些步骤按照一定的顺序执行。模板方法可以包含具体实现,也可以调用抽象方法或钩子方法。

  2. 抽象方法(Abstract Method)和钩子方法(Hook Method):
    抽象方法是模板方法中的一个或多个方法,它们由抽象类或接口定义,并延迟到子类中实现。子类必须提供这些抽象方法的具体实现。钩子方法是在模板方法中声明的一个空方法或具有默认实现的方法,子类可以选择性地重写钩子方法。

模板方法模式的核心思想是将算法的结构固定下来,但允许具体的实现细节在子类中进行扩展。通过定义模板方法和抽象方法,模板方法模式提供了一种框架,促使代码重用和扩展。

模板方法模式的优点包括:

  • 提供了一种固定算法结构,方便代码复用和维护。
  • 允许子类根据需要重写特定步骤,实现自己的行为。
  • 通过钩子方法,允许子类选择性地扩展或修改算法的行为。

模板方法模式适用于以下场景:

  • 当需要定义一个算法的骨架,但允许具体步骤的实现在子类中变化时。
  • 当希望通过继承和多态来实现代码重用和扩展时。
  • 当有一些通用操作可以放在父类中,而具体实现可以在子类中实现时。

需要注意的是,模板方法模式使用继承来实现算法的扩展,这可能导致类层次结构的膨胀。此外,由于模板方法在父类中被定义,因此子类对算法结构的修改受到限制。因此,在使用模板方法模式时需要仔细设计类的层次结构和方法的可扩展性。

模板方法模式的优缺点

模板方法模式是一种常用的设计模式,它在定义算法的骨架的同时,允许子类根据需要重写特定的步骤,以实现自己的行为。下面是模板方法模式的一些优缺点:

优点:

  1. 代码复用:模板方法模式通过将算法的结构固定在父类中,可以在子类中重用这个算法,避免了重复编写相同的代码。
  2. 提高了扩展性:通过在模板方法中定义抽象方法或钩子方法,子类可以根据需要实现自己的具体步骤,从而灵活地扩展或修改算法的行为。
  3. 降低了代码的重构风险:由于模板方法模式将算法的核心逻辑固定在父类中,只允许子类实现特定的步骤,因此在修改算法时,只需关注修改对应的步骤,减少了对整个算法的影响,降低了代码的重构风险。

缺点:

  1. 类层次结构的扩展:模板方法模式使用继承来实现算法的扩展,这可能导致类层次结构的膨胀。每个变体的实现都需要创建一个具体的子类,当变体较多时,类的数量会增加。
  2. 父类对子类的依赖:在模板方法模式中,父类定义了算法的骨架,而子类负责实现具体的步骤。这使得父类对子类有一定的依赖性,子类的实现可能会影响算法的整体结构。
  3. 不适合算法的所有步骤都需要扩展:如果算法的所有步骤都需要在子类中进行扩展,那么模板方法模式可能不适用。因为这样会导致在每个子类中重写所有的步骤,增加了代码的重复性和复杂性。

总的来说,模板方法模式在提供代码复用和扩展性方面具有一定的优势,但需要注意类层次结构的扩展和父类对子类的依赖。在使用模板方法模式时,需要仔细考虑算法的结构和步骤的扩展性,以及权衡代码的复用和灵活性之间的关系。

模板方法模式的应用场景

模板方法模式适用于以下场景:

  1. 算法的骨架固定:当有一个算法的基本骨架已经确定,但其中的某些具体步骤可以有不同的实现方式时,可以使用模板方法模式。例如,一个订单处理流程中,包括接收订单、验证订单、处理支付、发货等步骤,这些步骤的顺序是固定的,但每个步骤的具体实现方式可能会有所不同。

  2. 代码复用和扩展:当有多个类或方法存在相似的行为模式时,可以将这些相似的行为提取到父类中的模板方法中,以实现代码的复用。同时,通过允许子类重写特定的步骤,可以在不修改父类的情况下扩展或修改行为。

  3. 框架和库的设计:模板方法模式广泛应用于框架和库的设计中。框架提供一个抽象的模板方法,定义了整体的流程和结构,而具体的实现则由使用框架的开发者提供。这样可以保持框架的稳定性,并提供给开发者一定的灵活性。

  4. 算法和流程的变体:当存在多个算法或流程的变体时,可以使用模板方法模式。通过定义一个模板方法,将算法或流程的共同部分提取到父类中,而将变体的实现细节留给子类。这样可以在不同的子类中实现不同的变体,而不会影响整体的结构和逻辑。

总的来说,模板方法模式适用于具有固定结构但可变实现的场景,例如订单处理、框架和库设计、算法和流程的变体等。它提供了一种灵活的方式来定义算法的骨架,并允许子类根据需要进行扩展和修改。

代码实现模板方法模式

以下是一个简单的代码示例,展示了如何使用模板方法模式来实现一个炒菜的模板:

from abc import ABC, abstractmethodclass CookTemplate(ABC):def cook(self):self.prepareIngredients()self.boilWater()self.cookIngredients()self.serveDish()def prepareIngredients(self):print("准备食材")def boilWater(self):print("煮水")@abstractmethoddef cookIngredients(self):passdef serveDish(self):print("上菜")

在这个示例中,CookTemplate 是一个抽象类,定义了一个模板方法 cook(),该方法定义了炒菜的整体流程。它依次调用了准备食材(prepareIngredients())、煮水(boilWater())、炒食材(cookIngredients())和上菜(serveDish())的步骤。

prepareIngredients()boilWater() 是具体的步骤,它们在父类中已经有了默认的实现。而 cookIngredients() 是一个抽象方法,它需要在子类中进行具体的实现。

下面是一个具体的子类实现:

class StirFryCook(CookTemplate):def cookIngredients(self):print("炒菜")class BoilCook(CookTemplate):def cookIngredients(self):print("煮菜")

在这个示例中,StirFryCookBoilCook 是两个具体的子类,它们分别实现了 cookIngredients() 方法,用于具体的炒菜和煮菜步骤。

下面是示例的使用代码:

stir_fry_cook = StirFryCook()
stir_fry_cook.cook()boil_cook = BoilCook()
boil_cook.cook()

运行以上代码,将会得到如下输出:

准备食材
煮水
炒菜
上菜
准备食材
煮水
煮菜
上菜

通过模板方法模式,我们可以定义炒菜的整体流程,并在子类中实现具体的炒菜或煮菜步骤,从而实现了代码的复用和扩展。

外观模式

怎么理解外观模式

外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中一组复杂的接口。外观模式通过将复杂的子系统封装在一个简单的接口背后,简化了客户端与子系统之间的交互,提供了一个更高层次的接口,使得客户端更容易使用子系统。

理解外观模式可以从以下几个方面入手:

  1. 封装复杂性:外观模式通过封装一组复杂的接口,将其隐藏在一个简单的接口后面。它提供了一个高级别的接口,客户端只需要与外观对象进行交互,而无需了解内部的复杂实现细节。这样可以降低客户端的复杂性,并提供了一种简化接口的方式。

  2. 提供简化接口:外观模式提供了一个简化的接口,将多个子系统的功能组合在一起。客户端只需要调用外观对象的方法,而不需要直接与多个子系统的接口进行交互。这样简化了客户端的代码,减少了对子系统的依赖。

  3. 解耦客户端和子系统:外观模式将客户端与子系统之间解耦,客户端不需要知道子系统的具体实现和内部结构。当子系统发生变化时,只需调整外观对象的代码,而不会影响到客户端的代码。这提供了一种松耦合的方式,增加了系统的灵活性和可维护性。

  4. 提高可扩展性:外观模式对于添加新的子系统或更改现有子系统相对较容易。由于客户端只与外观对象进行交互,所以在添加新的子系统时,只需在外观对象中添加相应的方法即可。这样可以避免对客户端代码的修改,提高了系统的可扩展性。

总的来说,外观模式通过提供一个简化的接口,封装复杂的子系统,解耦客户端和子系统,提高了系统的可用性、可扩展性和可维护性。它是一种很好地组织和管理复杂系统的方式,使得客户端更容易使用和理解系统的功能。

外观模式的优缺点

外观模式(Facade Pattern)作为一种设计模式,具有以下优点和缺点:

优点:

  1. 简化接口:外观模式提供了一个简化的接口,将复杂的子系统隐藏起来,使得客户端更容易使用。客户端只需要与外观对象进行交互,而不需要了解子系统的内部结构和复杂性。

  2. 解耦客户端和子系统:外观模式将客户端与子系统解耦,客户端不需要直接与多个子系统进行交互。这样当子系统发生变化时,只需修改外观对象即可,而不会影响到客户端的代码。这提高了系统的灵活性和可维护性。

  3. 提高可扩展性:外观模式对于添加新的子系统或更改现有子系统相对较容易。由于客户端只与外观对象进行交互,所以在添加新的子系统时,只需在外观对象中添加相应的方法即可。这样可以避免对客户端代码的修改,提高了系统的可扩展性。

  4. 提高了系统的安全性:外观模式可以通过将敏感的子系统接口隐藏起来,提供一个受控的访问点。客户端只能通过外观对象来访问子系统,而无法直接调用子系统的方法。这样可以增加系统的安全性,防止客户端滥用或错误使用子系统的功能。

缺点:

  1. 不符合开闭原则:外观模式的缺点是当需要修改系统的功能时,可能需要修改外观对象的代码。如果系统中存在多个外观对象,那么修改的范围可能会更大。这违反了开闭原则(对扩展开放,对修改关闭),需要谨慎设计外观对象的接口。

  2. 可能引入性能问题:由于外观模式将多个子系统封装在一个接口中,可能会导致性能问题。如果某个子系统的操作非常复杂或耗时较长,那么在调用外观对象的方法时可能会出现性能瓶颈。在这种情况下,需要仔细考虑是否使用外观模式。

  3. 可能降低灵活性:外观模式的目的是简化接口,隐藏复杂性。但这也意味着客户端可能无法直接访问子系统的所有功能。如果客户端需要直接使用某个子系统的特定功能,而外观对象没有提供相应的方法,那么就无法通过外观模式实现。

需要根据具体的情况来评估外观模式的适用性。在设计系统时,应该权衡外观模式的优点和缺点,并根据具体需求和设计目标来决定是否使用外观模式。

外观模式的应用场景

外观模式(Facade Pattern)适用于以下场景:

  1. 简化复杂的子系统:当系统中存在复杂的子系统,其接口众多并且相互之间存在依赖关系时,可以使用外观模式来封装这些复杂性,提供一个简单的接口供客户端使用。外观模式可以隐藏子系统的复杂性,使得客户端更容易使用和理解系统的功能。

  2. 提供统一的接口:当系统中存在多个子系统,而客户端需要与这些子系统进行交互时,可以使用外观模式提供一个统一的接口。外观对象作为一个中间层,将客户端与子系统解耦,客户端只需要与外观对象进行交互,而不需要直接了解和调用多个子系统的接口。

  3. 系统的独立性和可移植性:当系统中的子系统发生变化时,如果客户端直接依赖于子系统的接口,那么客户端的代码需要修改。使用外观模式可以降低客户端与子系统的耦合度,当子系统发生变化时,只需要修改外观对象的代码,而不会影响到客户端的代码。这提高了系统的独立性和可移植性。

  4. 系统的安全性和访问控制:外观模式可以提供一个受控的访问点,限制客户端对子系统的访问。外观对象可以隐藏敏感的子系统接口,只提供有限的访问权限。这有助于提高系统的安全性,并防止客户端滥用或错误使用子系统的功能。

总的来说,外观模式适用于需要简化复杂子系统、提供统一接口、降低耦合度、提高系统的独立性和安全性的场景。它可以帮助简化客户端与子系统的交互,提供一个更高级别的接口,使得客户端更容易使用和理解系统的功能。

代码实现外观模式

以下是一个简单的外观模式的代码实现示例:

# 子系统A
class SubsystemA:def operationA(self):print("SubsystemA operation")# 子系统B
class SubsystemB:def operationB(self):print("SubsystemB operation")# 子系统C
class SubsystemC:def operationC(self):print("SubsystemC operation")# 外观类
class Facade:def __init__(self):self.subsystemA = SubsystemA()self.subsystemB = SubsystemB()self.subsystemC = SubsystemC()def operation(self):self.subsystemA.operationA()self.subsystemB.operationB()self.subsystemC.operationC()# 客户端代码
facade = Facade()
facade.operation()

在上述代码中,我们有三个子系统 (SubsystemA, SubsystemB, SubsystemC),每个子系统都有自己的操作(operationA, operationB, operationC)。然后我们创建了一个外观类 Facade,它封装了这些子系统,并提供了一个名为 operation 的方法,该方法在内部调用了子系统的相应操作。

在客户端代码中,我们创建了一个 Facade 对象,并调用了其 operation 方法。客户端只需要与外观对象进行交互,而不需要了解底层子系统的复杂性和实现细节。外观对象负责协调子系统的操作,并提供一个简单的接口给客户端使用。

这样,通过外观模式,我们可以将复杂的子系统封装起来,使得客户端更容易使用和理解系统的功能。当需要修改子系统时,只需修改外观类的代码,而不会影响到客户端的代码,从而提高了系统的灵活性和可维护性。

原型模式

怎么理解原型模式

原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制(克隆)现有对象来创建新对象,而不是通过使用构造函数直接创建。原型模式基于一个原型对象,通过复制该原型对象来创建新的对象,使得创建过程更加灵活和高效。

理解原型模式可以从以下几个方面来考虑:

  1. 对象的复制:原型模式的核心概念是对象的复制。原型对象作为一个可复制的对象,可以通过复制自身来创建新的对象。这种复制可以是浅复制(只复制对象的基本属性,引用类型属性仍然共享)或深复制(复制对象的所有属性和引用类型属性)。通过复制现有对象,可以避免重复创建相似对象的开销,提高对象的创建效率。

  2. 原型与实例的关系:原型对象作为一个样板,可以被多个实例所共享。当需要创建新的对象时,可以通过克隆原型对象来创建新的实例。原型模式允许动态地添加或修改原型对象,从而影响所有基于该原型创建的实例,实现了原型和实例之间的解耦。

  3. 原型注册与管理:原型模式通常会使用一个原型管理器(Prototype Manager)来注册和管理原型对象。原型管理器充当一个仓库,用于存储和获取原型对象。客户端可以通过原型管理器来获取所需类型的原型对象,并进行克隆创建新的实例。

  4. 适用性:原型模式适用于以下情况:当创建新对象的过程比较复杂或耗时时,可以使用原型模式来复制已有对象,避免重复的初始化过程;当需要动态地添加或修改对象的属性时,可以通过修改原型对象来影响所有创建的实例;当系统需要保护对象免于被修改时,可以使用原型模式,因为克隆的对象是独立的,不会影响到原型对象。

总结来说,原型模式通过克隆现有对象来创建新对象,提供了一种灵活、高效的对象创建方式。它可以避免重复的初始化过程,提高对象创建效率,同时也允许动态地添加或修改对象的属性。理解原型模式有助于在需要创建对象时选择适当的创建方式,并在系统中实现对象的复制和管理。

原型模式的优缺点

原型模式是一种创建型设计模式,它具有以下优点和缺点:

优点:

  1. 减少对象的创建成本:原型模式通过复制现有对象来创建新对象,避免了重复的初始化过程,从而提高了对象的创建效率。

  2. 灵活性和扩展性:通过原型模式,可以动态地添加或修改原型对象的属性,从而影响所有基于该原型创建的实例。这种灵活性使得系统更容易进行扩展和升级。

  3. 对象的创建与使用分离:原型模式将对象的创建和使用逻辑分离。客户端只需要通过原型对象来克隆新对象,而不需要关心具体的创建细节,降低了客户端与具体类之间的耦合度。

  4. 保护对象的不可变性:通过原型模式创建的对象是独立的,对其中一个对象的修改不会影响到其他对象。这种保护对象的不可变性有助于构建稳定的系统。

缺点:

  1. 对象的深复制可能较复杂:在原型模式中,对象的复制通常使用深复制来保证克隆对象的独立性。但是,深复制可能涉及到复杂的对象结构和引用关系,导致实现起来比较复杂。

  2. 需要为每个类实现克隆方法:在原型模式中,每个需要被克隆的类都需要实现克隆方法。对于大型系统或者对象较多的情况,可能需要为很多类都添加相应的克隆方法,增加了代码量。

  3. 克隆方法的正确性:为了正确地实现克隆方法,需要保证对象的所有属性都能被正确地复制。如果有些属性无法被克隆或者被克隆的结果不符合预期,可能会导致克隆出的对象与原始对象之间存在差异。

总体来说,原型模式在适当的场景下能够提供灵活的对象创建和复制机制,减少对象创建成本,同时也具有一些实施上的复杂性。在使用原型模式时,需要考虑对象结构的复杂性、克隆方法的正确性以及性能等因素。

原型模式的应用场景

原型模式适用于以下场景:

  1. 对象的创建成本高:当创建一个对象的过程非常耗时或复杂时,可以使用原型模式来复制现有对象来创建新的对象,从而避免重复的初始化工作,提高对象创建的效率。

  2. 对象的创建与使用分离:当对象的创建和使用逻辑相互耦合,难以进行独立维护和扩展时,可以使用原型模式来将对象的创建与使用分离。通过原型模式,可以将对象的创建逻辑放在原型对象中,而将对象的使用逻辑放在客户端中,从而降低耦合度并提高系统的灵活性。

  3. 动态配置对象:当需要根据不同的配置来创建对象时,可以使用原型模式。通过原型模式,可以创建一个原型对象,然后根据不同的配置对原型对象进行克隆,从而得到不同配置的对象实例。

  4. 保护对象的不可变性:当需要保护对象免于被修改时,可以使用原型模式。因为通过原型模式创建的对象是独立的,对其中一个对象的修改不会影响到其他对象。

  5. 原型管理与资源共享:当系统需要管理一组相似对象,并且这些对象之间共享一些资源时,可以使用原型模式。通过原型管理器,可以注册和管理一组原型对象,并在需要时克隆这些对象来创建新的实例。

总的来说,原型模式适用于需要复制现有对象来创建新对象的场景,特别是当创建对象的成本高、创建与使用逻辑耦合、需要动态配置对象、保护对象不可变性以及原型管理与资源共享等情况下。原型模式可以提高对象创建的效率、降低耦合度并增加系统的灵活性。

代码实现原型模式

下面是一个使用原型模式的简单代码示例:

import copy# 原型类
class Prototype:def __init__(self):self.name = "Prototype"def clone(self):return copy.deepcopy(self)# 客户端代码
prototype = Prototype()
print("Original Object:", prototype.name)# 克隆对象
clone_obj = prototype.clone()
print("Cloned Object:", clone_obj.name)

在上述代码中,定义了一个原型类 Prototype,它包含一个 clone 方法用于克隆自身。在 clone 方法中使用 copy.deepcopy() 函数进行深拷贝,确保克隆出的对象是独立的。

在客户端代码中,首先创建一个原型对象 prototype,输出其名称。然后通过调用 clone 方法克隆出一个新的对象 clone_obj,并输出其名称。可以看到,克隆出的对象与原型对象具有相同的属性值。

原型模式的关键在于克隆方法,通过克隆方法复制现有对象来创建新对象,而不是通过构造函数直接创建。这样可以避免重复的初始化过程,提高对象的创建效率,并确保克隆出的对象是独立的。

需要注意的是,在实际使用中,原型模式可能涉及到更复杂的对象结构和克隆逻辑。此处的示例只是一个简单的演示,以便理解原型模式的基本概念和实现方式。

策略模式

怎么理解策略模式

策略模式(Strategy Pattern)是一种行为型设计模式,它允许在运行时选择算法的行为,将算法封装成独立的策略对象,并使它们可以互相替换。策略模式将算法的选择与算法的实现分离,使得算法可以独立于客户端而变化,提供了一种灵活的解决方案。

理解策略模式可以从以下几个方面来考虑:

  1. 策略对象:策略模式中的策略对象是封装了具体算法的对象。每个策略对象实现了一种特定的算法,可以根据需要进行替换。策略对象通常具有一个公共的接口,以便于客户端与策略对象进行交互。

  2. 策略选择:策略模式允许在运行时动态地选择使用哪种算法。客户端可以根据具体情况选择合适的策略对象,并将其传递给上下文对象。上下文对象会在需要时调用策略对象的方法来执行相应的算法。

  3. 策略的替换:由于策略对象之间具有相同的接口,因此它们可以互相替换,而不会对客户端代码造成影响。这使得客户端能够灵活地改变算法的行为,而无需修改原有的代码。

  4. 适用性:策略模式适用于以下情况:当一个系统需要在多个算法中选择一种,且这些算法之间的差异较大时;当需要在运行时动态地切换算法时;当一个类有多个行为变体,而不希望使用条件语句来实现时。

总结来说,策略模式通过将算法封装成独立的策略对象,使得算法的选择与算法的实现分离,提供了一种灵活的解决方案。客户端可以在运行时选择合适的策略对象,并将其传递给上下文对象来执行相应的算法。理解策略模式有助于在需要根据不同的情况选择不同的算法时,设计灵活、可扩展的系统。

策略模式的优缺点

策略模式(Strategy Pattern)具有以下优点和缺点:

优点:

  1. 算法的独立性:策略模式将每个算法封装在独立的策略对象中,使得算法可以独立于客户端而变化。这样可以方便地添加、删除或修改算法,而不会影响到客户端代码。

  2. 可扩展性:由于策略模式将算法的选择与算法的实现分离,添加新的策略对象非常方便。可以根据需要定义新的策略对象,并在运行时动态地选择使用哪种策略对象,从而实现系统的可扩展性。

  3. 简化复杂的条件语句:使用策略模式可以避免使用大量的条件语句来选择不同的算法。将算法封装在独立的策略对象中,使得客户端代码更加清晰,易于理解和维护。

  4. 提高代码的复用性:策略模式中的策略对象可以在不同的上下文中重复使用,从而提高代码的复用性。同样的策略对象可以被多个客户端使用,避免了重复编写相同的代码。

缺点:

  1. 增加类的数量:使用策略模式会引入多个策略对象,这可能会增加类的数量。如果算法较少或者简单,引入策略模式可能会使代码变得复杂,增加系统的复杂性。

  2. 客户端需要了解不同的策略对象:客户端需要了解不同的策略对象以及它们之间的区别,以便在运行时进行选择。这可能增加了客户端代码的复杂性。

  3. 上下文对象的管理:策略模式中,上下文对象需要持有一个策略对象,并在需要时调用策略对象的方法。这可能需要一定的管理和协调工作,特别是在多线程环境下可能存在并发访问的问题。

综上所述,策略模式通过将算法封装成独立的策略对象,提供了一种灵活、可扩展的解决方案。它具有算法的独立性、可扩展性以及简化复杂的条件语句等优点。然而,需要注意增加类的数量、客户端了解不同策略对象以及上下文对象的管理等缺点。在具体应用策略模式时,需要权衡考虑这些优缺点,并根据实际需求进行设计和实施。

策略模式的应用场景

策略模式(Strategy Pattern)适用于以下场景:

  1. 算法的多样性:当系统中存在多个算法,并且这些算法之间可以相互替换,而且每个算法都可以独立于客户端而变化时,可以使用策略模式。例如,一个电商平台的结算系统可以根据不同的促销策略(如满减、折扣、赠品等)来计算订单的最终金额。

  2. 条件语句的复杂性:当系统中存在大量的条件语句来选择不同的算法时,可以考虑使用策略模式来简化代码。策略模式将每个算法封装在独立的策略对象中,使得客户端代码更加清晰、易于理解和维护。

  3. 运行时动态选择算法:当需要在运行时动态地选择使用哪种算法时,可以使用策略模式。通过将不同的算法封装在独立的策略对象中,客户端可以根据具体情况选择合适的策略对象,并将其传递给上下文对象来执行相应的算法。

  4. 系统的扩展性:当系统需要支持新的算法或者变体时,策略模式可以提供一种灵活的解决方案。通过添加新的策略对象,可以方便地扩展系统的功能,而不需要修改已有的代码。

  5. 避免使用继承扩展功能:当需要扩展或变化的是算法而不是对象本身时,策略模式比继承更加灵活。策略模式通过组合和委托来实现算法的扩展,而不是通过继承,避免了继承带来的静态耦合。

总而言之,策略模式适用于存在多个算法、需要动态选择算法、需要简化复杂的条件语句、支持系统的扩展性以及避免使用继承扩展功能的场景。它提供了一种灵活、可扩展的算法选择解决方案,可以提高代码的可维护性和可复用性。

代码实现策略模式

下面是一个使用策略模式的简单代码示例,以展示如何在实践中实现策略模式:

# 定义策略接口
class PaymentStrategy:def pay(self, amount):pass# 定义具体的策略类
class CreditCardPaymentStrategy(PaymentStrategy):def pay(self, amount):print("Paid {} amount using credit card.".format(amount))class PayPalPaymentStrategy(PaymentStrategy):def pay(self, amount):print("Paid {} amount using PayPal.".format(amount))class AliPayPaymentStrategy(PaymentStrategy):def pay(self, amount):print("Paid {} amount using AliPay.".format(amount))# 定义上下文类
class ShoppingCart:def __init__(self, payment_strategy):self.payment_strategy = payment_strategydef pay(self, amount):self.payment_strategy.pay(amount)# 客户端代码
if __name__ == '__main__':# 创建具体的策略对象credit_card_strategy = CreditCardPaymentStrategy()paypal_strategy = PayPalPaymentStrategy()alipay_strategy = AliPayPaymentStrategy()# 创建上下文对象,并设置具体的策略对象shopping_cart = ShoppingCart(credit_card_strategy)# 进行支付shopping_cart.pay(100)# 动态切换策略对象shopping_cart.payment_strategy = paypal_strategyshopping_cart.pay(200)shopping_cart.payment_strategy = alipay_strategyshopping_cart.pay(300)

在这个示例中,我们定义了一个策略接口 PaymentStrategy,并实现了具体的策略类 CreditCardPaymentStrategyPayPalPaymentStrategyAliPayPaymentStrategy。这些具体的策略类实现了 pay 方法来执行相应的支付操作。

我们还定义了一个上下文类 ShoppingCart,它接收一个策略对象作为参数,并在 pay 方法中调用策略对象的 pay 方法来进行支付操作。

在客户端代码中,我们创建了具体的策略对象,并将其传递给上下文对象 ShoppingCart。然后,我们可以通过调用 pay 方法来执行支付操作。通过动态切换策略对象,我们可以在运行时选择不同的支付策略。

运行以上代码,将输出以下结果:

Paid 100 amount using credit card.
Paid 200 amount using PayPal.
Paid 300 amount using AliPay.

这个示例展示了如何使用策略模式将不同的支付方式封装成独立的策略对象,并根据需要动态地选择使用哪种支付策略。这样,客户端代码可以方便地调用相应的支付策略,而不需要关心具体的实现细节。
祝大家新年快乐,学业有成,万事如意!🚀
嘿嘿都看到这里啦,乖乖 点个赞吧

相关文章:

【后端高频面试题--设计模式下篇】

🚀 作者 :“码上有前” 🚀 文章简介 :后端高频面试题 🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬 后端高频面试题--设计模式下篇 往期精彩内容设计模式总览模板方法模式怎么理解模板方法模式模板方…...

这才是大学生该做的副业,别再痴迷于游戏了!

感谢大家一直以来的支持和关注,尤其是在我的上一个公众号被关闭后,仍然选择跟随我的老粉丝们,你们的支持是我继续前行的动力。为了回馈大家长期以来的陪伴,我决定分享一些实用的干货,这些都是我亲身实践并且取得成功的…...

Ubuntu20.04 安装jekyll

首先使根据官方文档安装:Jekyll on Ubuntu | Jekyll • Simple, blog-aware, static sites 如果没有报错,就不用再继续看下去了。 我这边在执行gem install jekyll bundler时报错,所以安装了rvm,安装rvm可以参考这篇文章Ubuntu …...

AWK语言

一. awk awk:报告生成器,格式化输出。 在 Linux/UNIX 系统中,awk 是一个功能强大的编辑工具,逐行读取输入文本,默认以空格或tab键作为分隔符作为分隔,并按模式或者条件执行编辑命令。而awk比较倾向于将一行…...

精通Nmap:网络扫描与安全的终极武器

一、引言 Nmap,即NetworkMapper,是一款开源的网络探测和安全审计工具。它能帮助您发现网络中的设备,并识别潜在的安全风险。在这个教程中,我们将一步步引导您如何有效地使用Nmap,让您的网络更加安全。 因为Nmap还有图…...

Java 学习和实践笔记(11)

三大神器&#xff1a; 官方网址: http://www.jetbrains.com/idea/ 官方网址: https://code.visualstudio.com/ 官方网址: http://www.eclipse.org 装好了idea社区版&#xff0c;并试运行以下代码&#xff0c;OK&#xff01; //TIP To <b>Run</b> code, press &l…...

开发实体类

开发实体类之间先在pom文件中加入该依赖 <!-- 开发实体类--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency>我们在实体类中声明各个属…...

人工智能学习与实训笔记(十五):Scikit-learn库的基础与使用

人工智能专栏文章汇总&#xff1a;人工智能学习专栏文章汇总-CSDN博客 本篇目录 一、介绍 1. 1 Scikit-learn的发展历程及定义 1.2 理解算法包、算法库及算法框架之间的区别和联系 二、Scikit-learn官网结构 三、安装与设置 3.1 Python环境的安装与配置 3.2 Scikit-lea…...

插值与拟合算法介绍

在数据处理和科学计算领域,插值与拟合是两种极为重要的数据分析方法。它们被广泛应用于信号处理、图像处理、机器学习、金融分析等多个领域,对于理解和预测数据趋势具有至关重要的作用。本文将深入浅出地介绍这两种算法的基本原理,并结合C语言编程环境探讨如何在CSDN开发者社…...

下一代Windows系统曝光:基于GPT-4V,Agent跨应用调度,代号UFO

下一代Windows操作系统提前曝光了&#xff1f;&#xff1f; 微软首个为Windows而设的智能体&#xff08;Agent&#xff09; 亮相&#xff1a; 基于GPT-4V&#xff0c;一句话就可以在多个应用中无缝切换&#xff0c;完成复杂任务。整个过程无需人为干预&#xff0c;其执行成功…...

二.自定义头文件

一.Worker.h 1.1概述 - 类名&#xff1a;Worker - 继承关系&#xff1a;所有其他类&#xff08;Employee、Manager、Boss&#xff09;都继承自该抽象类 - 头文件保护&#xff1a;使用 pragma once 防止头文件重复包含 - 引入标准库&#xff1a;包含 <iostream> 和 <st…...

【AIGC】Stable Diffusion之模型微调工具

推荐一款好用的模型微调工具&#xff0c;cybertron furnace 是一个lora训练整合包&#xff0c;提供训练 lora 模型的工具集或环境。集成环境包括必要的依赖项和配置文件、预训练脚本&#xff0c;支持人物、二次元、画风、自定义lora的训练&#xff0c;以简化用户训练 lora 模型…...

探索未来科技前沿:深度学习的进展与应用

深度学习的进展 摘要&#xff1a;深度学习作为人工智能领域的重要分支&#xff0c;近年来取得了巨大的进展&#xff0c;并在各个领域展现出惊人的应用潜力。本文将介绍深度学习的发展历程、技术原理以及在图像识别、自然语言处理等领域的应用&#xff0c;展望深度学习在未来的…...

PTA | Wifi密码

下面是微博上流传的一张照片&#xff1a;“各位亲爱的同学们&#xff0c;鉴于大家有时需要使用 wifi&#xff0c;又怕耽误亲们的学习&#xff0c;现将 wifi 密码设置为下列数学题答案&#xff1a;A-1&#xff1b;B-2&#xff1b;C-3&#xff1b;D-4&#xff1b;请同学们自己作答…...

Linux中gdb使用说明书

首先我们要使用gdb&#xff0c;必须明白gdb使用范围&#xff1a; 要使用gdb调试&#xff0c;必须在源代码生成二进制程序的时候, 加上 -g 选项&#xff08;gcc/g) 其次&#xff0c;我们就要来学习gdb使用的一些命令了&#xff1a; list&#xff0f;l 行号&#xff1a;显…...

LInux——开发工具的使用

目录 Linux软件包管理器 yum rzsz Linux编辑器——vim vim的使用 vim的基本操作 命令模式的常见命令 底行模式的常见命令 vim是需要配置的 Linux编译器——gcc/g 预处理 编译 汇编 链接 函数库 Linux项目自动化构建工具 make/makefile make原理 项目清理 Linux调试器g…...

沁恒CH32V30X学习笔记03--64位systick

systick CH32F2x 系列产品Cortex-M3 内核自带了一个 24 位自减型计数器(SysTick timer)。支持 HCLK 或 HCLK/8 作为时基,具有较高优先级别(6)。一般可用于操作系统的时基。 CH32V3x 系列产品内核自带了一个 64 位加减计数器(SysTick),支持 HCLK 或者 HCLK/8 作为时基,…...

【JavaEE】IP协议

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…...

计算机网络-数据通信基础

目录 前言 一、数据通信基本概念 二、数据通信相关知识1 总结 前言 正在学习计算机网络体系&#xff0c;把每日所学的知识梳理出来&#xff0c;既能够当作读书笔记&#xff0c;又能分享出来和大家一同学习讨论。 一、数据通信基本概念 基本概念&#xff1a;信源、信道、信宿&…...

【lesson53】线程控制

文章目录 线程控制 线程控制 线程创建 代码&#xff1a; 运行代码&#xff1a; 强调一点&#xff0c;线程和进程不一样&#xff0c;进程有父进程的概念&#xff0c;但在线程组里面&#xff0c;所有的线程都是对等关系。 错误检查: 传统的一些函数是&#xff0c;成功返回0&…...

TypeScript(一):TypeScript基本理解

TypeScript基本理解 为什么使用TS JavaScript发展至今&#xff0c;没有进行数据类型的验证而我们知道&#xff0c;在编程阶段&#xff0c;错误发现的越早越好而TS就解决了JS的这个问题 认识TypeScript TypeScript是拥有类型的JavaScript超级&#xff0c;它可以编译成普通、…...

C语言—指针

碎碎念:做指针题的时候我仿佛回到了原点&#xff0c;总觉得目的是为了把框架搭建起来&#xff0c;我胡说的哈31 1.利用指针变量将一个数组中的数据反向输出。 /*1.利用指针变量将一个数组中的数据反向输出。*/#include <stdio.h> #include <time.h> #include <…...

c++作业

Shell中的函数&#xff08;先调用后使用的原则&#xff09;&#xff08;没有申明&#xff09; &#xff08;Function&#xff09; 函数名&#xff08;有没有参数根据调用格式&#xff09;&#xff08;不能写任何内容&#xff09; { 函数体 Return 返回值 } 函数名 ----》…...

什么是tomcat?tomcat是干什么用的?

前言 Tomcat是一个开源的、轻量级的应用服务器&#xff0c;是Apache软件基金会的一个项目。它实现了Java Servlet、JavaServer Pages&#xff08;JSP&#xff09;和Java Expression Language&#xff08;EL&#xff09;等Java技术&#xff0c;用于支持在Java平台上运行的动态W…...

中科院一区论文复现,改进蜣螂算法,Fuch映射+反向学习+自适应步长+随机差分变异,MATLAB代码...

本期文章复现一篇发表于2024年来自中科院一区TOP顶刊《Energy》的改进蜣螂算法。 论文引用如下&#xff1a; Li Y, Sun K, Yao Q, et al. A dual-optimization wind speed forecasting model based on deep learning and improved dung beetle optimization algorithm[J]. Ener…...

C# 如何实现一个事件总线

EventBus&#xff08;事件总线&#xff09;是一种用于在应用程序内部或跨应用程序组件之间进行事件通信的机制。 它允许不同的组件通过发布和订阅事件来进行解耦和通信。在给定的代码片段中&#xff0c;我们可以看到一个使用C#实现的Event Bus。它定义了一些接口和类来实现事件…...

Python学习路线图

防止忘记&#xff0c;温故知新 进阶路线...

作业2.14

chgrp: 只能修改文件的所属组 chgrp 新的组 文件名 要求&#xff1a;修改的目标组已经存在 chown: chown 新的用户名 文件名 sudo chown root &#xff1a;1 将文件1的所属组用户和所属组用户都改为root sudo chown root&#xff1a;ubuntu 1 将文件1的所属用户…...

基于python+django+mysql的小区物业管理系统

该系统是基于pythondjango开发的小区物业管理系统。适用场景&#xff1a;大学生、课程作业、毕业设计。学习过程中&#xff0c;如遇问题可以在github给作者留言。主要功能有&#xff1a;业主管理、报修管理、停车管理、资产管理、小区管理、用户管理、日志管理、系统信息。 演示…...

控制与状态机算法

控制与状态机算法是计算机科学、电子工程和自动化领域中常用的一种设计工具,它用来描述一个系统的行为,该系统在不同时间点可以处于不同的状态,并且其行为取决于当前状态以及输入的信号或事件。状态机算法的核心概念包括: 状态(State):系统的任何可能配置。每个状态代表…...

sql常用语句小结

创建表&#xff1a; create table 表名&#xff08; 字段1 字段类型 【约束】【comment 字段1注释】&#xff0c; //【】里面的东西可以不用加上去 字段2 字段类型 【约束】【comment 字段2注释】 &#xff09;【comment 表注释】 约束&#xff1a;作用于表中字段上的规则…...

云计算基础-虚拟机迁移原理

什么是虚拟机迁移 虚拟机迁移是指将正在运行的虚拟机实例从一个物理服务器&#xff08;或主机&#xff09;迁移到另一个物理服务器&#xff08;或主机&#xff09;的过程&#xff0c;而不会中断虚拟机的运行。 虚拟机拟机迁移分类虚 热迁移&#xff1a;开机状态下迁移 冷迁…...

云计算基础-云计算概念

云计算定义 云计算是一种基于互联网的计算方式&#xff0c;通过这种计算方式&#xff0c;共享的软硬件资源和信息可以按需提供给计算机和其他设备。云计算依赖资源共享以达成规模经济&#xff0c;类似基础设置(如电力网)。 云计算最基本的概念就是云加端&#xff0c;我们有一个…...

如何将阿里云服务器迁移

&#x1f4d1;前言 本文主要是如何将阿里云服务器迁移实现数据转移的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️** &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日…...

如何将本地的python项目部署到linux服务器中

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂 。 前言 本地写好的python项目&#xff0c;如何部署在服务器上运行呢&#xff1f;今天&#xff0c;我们就来抽一点点时间来看看。&#xff08;网上找的资料&#xff0c;大部分都囫囵吞枣的…...

每日五道java面试题之java基础篇(五)

目录&#xff1a; 第一题. final、finally、finalize 的区别&#xff1f;第二题. 和 equals 的区别&#xff1f;第三题.hashCode 与 equals?第四题. Java 是值传递&#xff0c;还是引⽤传递&#xff1f;第五题 深拷贝和浅拷贝&#xff1f; 第一题. final、finally、finalize 的…...

HiveSQL——用户行为路径分析

注&#xff1a;参考文档&#xff1a; SQL之用户行为路径分析--HQL面试题46【拼多多面试题】_路径分析 sql-CSDN博客文章浏览阅读2k次&#xff0c;点赞6次&#xff0c;收藏19次。目录0 问题描述1 数据分析2 小结0 问题描述已知用户行为表 tracking_log&#xff0c; 大概字段有&…...

专利的申请

申请发明或者实用新型专利的&#xff0c;应当提交请求书、说明书及其摘要和权利要求书等文件。 请求书应当写明发明或者实用新型的名称&#xff0c;发明人或者设计人的姓名&#xff0c;申请人姓名或者名称、地址&#xff0c;以及其他事项。 说明书应当对发明或者实用新型作出清…...

嵌入式学习 C++ Day5、6

嵌入式学习 C Day5、6 一、思维导图 二、作业 1.以下是一个简单的比喻&#xff0c;将多态概念与生活中的实际情况相联系&#xff1a; 比喻&#xff1a;动物园的讲解员和动物表演 想象一下你去了一家动物园&#xff0c;看到了许多不同种类的动物&#xff0c;如狮子、大象、猴…...

阿里云香港服务器cn2速度测试和租用价格表

阿里云香港服务器中国香港数据中心网络线路类型BGP多线精品&#xff0c;中国电信CN2高速网络高质量、大规格BGP带宽&#xff0c;运营商精品公网直连中国内地&#xff0c;时延更低&#xff0c;优化海外回中国内地流量的公网线路&#xff0c;可以提高国际业务访问质量。阿里云服务…...

《学成在线》微服务实战项目实操笔记系列(P92~P120)【下】

史上最详细《学成在线》项目实操笔记系列【下】&#xff0c;跟视频的每一P对应&#xff0c;全系列18万字&#xff0c;涵盖详细步骤与问题的解决方案。如果你操作到某一步卡壳&#xff0c;参考这篇&#xff0c;相信会带给你极大启发。 四、课程发布模块 4.1 (课程发布)模块需求…...

php数据类型以及运算符、判断条件

php数据类型以及运算符 1. php数据类型2. 使用举例3. 运算符4. 判断条件if else elseif 1. php数据类型 包括 String(字符串)、Integer(整型)、Float(浮点型)、Boolean(布尔型)、Array(数组)、Object(对象)、NULL(空值) 2. 使用举例 1.字符串 2.整型 3.浮点型 4.布尔型 5.数组…...

大数据01-导论

零、文章目录 大数据01-导论 1、数据与数据分析 **数据&#xff1a;是事实或观察的结果&#xff0c;是对客观事物的逻辑归纳&#xff0c;是用于表示客观事物的未经加工的原始素材。**数据可以是连续的值&#xff0c;比如声音、图像&#xff0c;称为模拟数据&#xff1b;也可…...

智能网卡(SmartNIC):增强网络性能

在当今的数字时代&#xff0c;网络性能和数据安全是各行各业面临的关键挑战。智能网卡是一项颠覆性的技术创新&#xff0c;对增强网络性能和加强数据安全性具有关键推动作用。本文旨在探讨智能网卡的工作原理及其在不同应用场景中的重要作用。 什么是智能网卡&#xff1f; 智…...

算法刷题day14

目录 引言一、平均二、三国游戏三、松散子序列 引言 今天做了三道新题&#xff0c;类型是贪心、枚举、DP&#xff0c;不是特别难&#xff0c;但是努力一下刚好能够够得上&#xff0c;还是不错的&#xff0c;只要能够一直坚持下去&#xff0c;不断刷题不断总结&#xff0c;就是…...

个性签名大全

只许一生浮世清欢愿我以孤独作为铠甲&#xff0c;自此不再受伤愿我是阳光&#xff0c;明媚而不忧伤我不敢太勇敢太执着太骄傲&#xff0c;我怕失去开始你是我的天使&#xff0c;最后你是我的唯一姐的霸气&#xff0c;无人能比&#xff0c;哥的傲气&#xff0c;无人能朋唯有万事…...

前端常用代码整理(不断更新中)— js,jquery篇(2)

目录 1.随机生成字符串 2.删除数组中重复元素 3.RGB到十六进制转换机制 4.打乱一个数组&#xff0c;重新组合 5.获取两个日期的时间间隔 &#xff08;天数&#xff09; 6.获取当天属于今年的第几天 7.截取字符串长度,超过部分显示为 ... 8.判断数组是否为空 9.英文句子首…...

普中51单片机学习(六)

点亮第一个LED LED相关知识 LED,即发光二极管&#xff0c;是一种半导体固体发光器件。工作原理为&#xff1a;LED的工作是有方向性的&#xff0c;只有当正级接到LED阳极&#xff0c;负极接到LED的阴极的时候才能工作&#xff0c;如果反接LED是不能正常工作的。其原理图如下 …...

visual studio注册码

最近在研究c/c 安装visual studio 需要注册 技术博客http://idea.coderyj.com/ 注册码 Visual Studio 2022(VS2022)激活码&#xff1a; Pro&#xff08;专业版&#xff09;: TD244-P4NB7-YQ6XK-Y8MMM-YWV2J Enterprise&#xff08;企业版&#xff09;: VHF9H-NXBBB-638P6-6JHC…...

Studio One 6.5下载安装激活图文教程

Studio One 6.5是由PreSonus公司打造一款功能强大的数字音乐创作软件&#xff0c;不仅为用户们提供了制作、混合、掌握和执行所有操作&#xff0c;还提供了简洁直观的主界面&#xff0c;因此使用起来也是十分的简单&#xff0c;就算是初学者也可以快速的上手使用起来&#xff0…...