Skip to content

三种设计模式

创建型

  • 单例模式

    • 保证一个类只有一个实例,并提供一个全局访问点以便于获取该实例
    • 懒汉式单例模式是在第一次使用时创建实例对象,而饿汉式单例模式是在程序启动时就创建实例对象。
    • 懒汉式实现方式在多线程环境下可能会出现线程安全的问题:
      • 因为多个线程可能同时进入 if instance == nil 的判断条件,从而创建出多个实例。
      • 通过使用 sync.Once 来保证只有一个 goroutine 执行初始化代码,从而解决了线程安全问题。
    • 单例模式具有以下特点和优点:
      • 避免重复创建实例,节省系统资源
  • 工厂方法模式

    • 提供了一种将对象的创建过程封装在子类中的方式,这样,客户端调用具体工厂类的工厂方法来创建对象:
    • 优点
      • 将对象的创建和使用解耦,客户端只需关心工厂方法和抽象产品接口。
      • 可以通过添加新的具体工厂类和具体产品类来扩展系统,符合开闭原则。
      • 封装了对象的创建过程,使系统更加灵活和可扩展。
      • 提供了一种标准化的产品创建方式,方便了系统的维护和扩展。
      • 隐藏了具体产品类的实现细节,减少了客户端对具体类的依赖和耦合。
    • 适用场景
      • 客户端不依赖具体产品类,而是依赖于抽象产品接口。
      • 客户端需要根据不同的条件动态创建不同的产品对象。
      • 需要对产品对象的创建过程进行封装和隐藏。
    • 实现过程:
      • 实现步骤1:定义抽象产品接口
      • 实现步骤2:创建具体产品实现类
      • 实现步骤3:定义抽象工厂接口
      • 实现步骤4:创建具体工厂实现类
      • 实现步骤5:客户端调用工厂方法创建产品对象
  • 抽象工厂模式

    • 供了一种创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类;
    • 优点
      • 将对象的创建与使用分离,客户端只需关心接口而不需要关心具体实现。
      • 可以遵循单一职责原则和开闭原则,易于扩展和维护。
      • 提供了一种统一的方式来创建一系列产品,让产品之间的关联更加灵活。
    • 应用场景
      • 当系统需要一系列相互关联或相互依赖的对象时,可以使用抽象工厂模式来统一创建。
      • 需要一个产品系列的对象,而不关心具体实现时,可以使用抽象工厂模式来创建该系列的对象。
    • 实现过程:
      • 实现步骤1: 需要定义一个抽象工厂接口,该接口声明了创建抽象产品的方法。
      • 实现步骤2: 定义具体的工厂类,该类实现了抽象工厂接口中的方法,用于创建具体的产品。
      • 实现步骤3: 定义一个抽象产品接口,该接口声明了产品的方法。
      • 实现步骤4: 定义具体的产品类,这些类实现了抽象产品接口中的方法。
      • 实现步骤5: 客户端使用抽象工厂和产品
  • 建造者模式

    • 按照步骤创建复杂对象,它将对象的构建与其表示分离,可以根据所需的组合配置不同的属性和参数:
    • 使用接口和结构体来实现建造者模式的基本结构
    • 用ConcreteBuilder的方法来构建Product对象里面的属性,给ConcreteBuilder里面调用具体Product对象的方法;
    • 优点:
      • 封装了对象的创建和组装过程,使客户端代码与具体的构建过程分离,更加灵活和可维护。
      • 可以通过不同的建造者组合配置不同的属性和参数,以创建不同的对象。
      • 可以避免在构造函数中使用过多的参数,使得代码更加简洁。
  • 原型模式

    • 通过复制现有对象来创建新对象,而无需使用new操作符。这种方式利用了对象之间的克隆关系,将对象的创建和使用分离开来。
    • 原型模式适用于以下场景
      • 对象的创建比较复杂,但是通过复制已有对象的方式可以更高效地创建对象。通过克隆对象可以节省创建时间。
      • 需要动态地创建或克隆对象,而不是通过直接创建新的对象实例。
    • 实现过程
      • 实现步骤1: 创建一个原型接口;
      • 实现步骤2: 使用原型接口创建和克隆对象;
      • 实现步骤3: 创建原型管理类,原型管理类负责创建和管理原型对象。
    • 特点和优点
      • 特点:
        • 允许对象在运行时动态创建。
        • 可以减少对象的创建时间,提高系统性能。
        • 对象的创建和使用相分离,便于管理和扩展。
      • 优点:
        • 提高对象的创建效率。
        • 简化对象的创建过程。
        • 可以动态地增加或减少对象。

结构型

  • 适配器模式

    • 将一个类的接口转换成客户端所期望的另一个接口,适配器模式使得那些原本由于接口不兼容而不能一起工作的类可以协同工作。
    • 实现过程
      • 实现步骤1: 设计适配器接口,用于适配器实现具体调用方法;
      • 实现步骤2: 实现适配器类,
      • 实现步骤3: 客户端代码调用适配器来实现功能;
    • 适配器模式和桥接模式“都可以用于处理两个不同类的接口问题”,但它们的主要区别在于
      • 适配器模式主要关注的是两个已经存在的接口之间的兼容性转换。
      • 桥接模式主要关注的是抽象与实现的分离,通过接口和实现类之间的桥接来实现解耦。
    • 应用场景:
      • 在微服务架构中,不同的微服务可能使用不同的接口进行通信。适配器模式可以帮助我们解决不同服务之间的接口兼容性问题;
      • 在前后端分离开发中的应用,可以帮助我们将后端的接口适配成前端所期望的接口。
  • 桥接模式

    • 将抽象与实现解耦,使得两者可以独立地变化,简单地说,桥接模式是一种让抽象和实现分离的方案。
    • 桥接模式的主要目的是将抽象部分与其实现部分解耦,以便二者可以独立地进行变化和扩展。这是通过建立一个抽象的桥接类完成的,该类链接到一个具体实现类。
    • 优点:
      • 提高了系统的可扩展性,抽象和实现都可以独立扩展,不会影响到对方。
      • 符合开闭原则,抽象部分和实现部分可以分别独立扩展,而不会影响到彼此。
      • 实现细节对客户透明,可以对用户隐藏实现细节。
    • 应用场景
      • 当你想要分离一个复杂对象的实现和抽象的时候,可以使用桥接模式。这可能会对现有代码的性能有好影响,尤其是当程序在运行时只使用了对象的一部分。
      • 当你需要在多个对象间分享特定的实现状态,但对于客户方代码而言,它们需要呈现为独立类。
    • 实现过程:
      • 1、定义抽象角色接口
      • 2、实现具体角色类,两个实现类;
      • 3、定义抽象桥接接口,包含角色接口;
      • 4、实现具体桥接类,通过角色接口的方法调用;
      • 5、客户端调用
  • 组合模式

    • 它是将对象组合成树状结构以表示“部分-整体”的层次结构,使得客户端以一致的方式处理单个对象和对象组合。
    • 优点有:
      • 可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,使得增加新构件也更容易。
      • 提供一个统一的接口,使得访问组合部分和单个对象的接口一致,使客户端对单个对象和组合对象的使用是一致的。
    • 实现过程:
      • 设计一个电商应用,商品目录就是一个很好的组合模式例子。一个类别可以包含其他类别,也可以包含商品(如电子类别包含了手机,电脑,而手机又包含了苹果手机,三星手机等)。
      • 实现步骤1: 定义抽象组件类
      • 实现步骤2: 定义叶子组件类
      • 实现步骤3: 定义容器组件类
      • 实现步骤4: 客户端调用
  • 装饰器模式

    • 动态地向一个对象添加额外的功能,而无需修改对象的代码。装饰器模式通过将对象包装在装饰器类中,实现在运行时添加、修饰或修改对象行为的能力。
    • 实现过程
      • 实现步骤1: 定义接口和实现类
      • 实现步骤2: 定义装饰器,实现上一步定义的接口;
      • 实现步骤3: 装饰器的实现,实现第一步定义的接口,交给第二步的装饰器使用;
      • 实现步骤4: 装饰器的使用
    • 装饰器模式和其他设计模式的比较
      • 装饰器模式与继承相比:它可以动态地添加功能而无需修改已有的代码。而继承则是静态的,需要在编译时确定。
      • 装饰器模式与静态代理模式:都可以实现功能扩展,但装饰器模式更加灵活,可以动态添加和移除功能。
      • 装饰器模式和动态代理模式:都可以在运行时对对象进行功能扩展,但装饰器模式是对单个对象进行装饰,而动态代理模式是代理对象与被代理对象之间的间接访问。
    • 优点:
      • 符合开闭原则,可动态添加和移除装饰器;
      • 动态扩展对象的功能,无需修改对象代码;
    • 应用场景:
      • 动态添加日志记录功能
      • 动态添加缓存功能
      • 动态数据校验
  • 外观模式

    • 提供了一个统一的接口,用于访问子系统中的一组接口。它隐藏了子系统的复杂性,为外部提供了一个简化的接口。
    • 实现过程:
      • 实现步骤1: 定义外观类,就是封装几个子类放在外观类一个方法里面,给客户端调用;
      • 实现步骤2: 定义子系统类,子类各自实现方法咯;
      • 实现步骤3: 使用外观模式实现客户端代码
    • 在实际开发中的应用场景:
      • 提供简化的接口来访问复杂的第三方库或API。
      • 封装一组复杂的逻辑操作,以简化客户端的调用过程。
    • 优点:
      • 将客户端与子系统的耦合度降低,客户端只需要与外观类进行交互,而不需要了解子系统的具体实现细节。
      • 符合开闭原则,可以很方便地增加或修改子系统中的功能。
  • 享元模式

    • 主要目的是最小化共享对象的数量,从而节省内存和提高性能。享元模式通过共享相同或相似的对象来减少对象的创建和消耗,以达到性能优化的效果。
    • 与其他设计模式相比,享元模式主要关注对象的共享和复用。它将对象分为可共享的内部状态和不可共享的外部状态。通过共享内部状态,可以减少对象的创建和内存占用;
    • 优点:
      • 最小化内存使用:通过共享相同或相似的对象,减少了内存的消耗。
      • 提高性能:减少对象的创建和销毁操作,加快了系统的运行速度。
      • 支持大量细粒度的对象:可以创建大量细粒度的对象,而不会占用过多的内存空间。
      • 简化系统结构:将对象的内部状态和外部状态分离,简化了系统的结构和复杂度。
    • 可以在如下场景中应用:
      • 游戏中的粒子对象:可以将每个粒子对象的属性分为内部状态和外部状态,共享具有相同属性的粒子对象。
      • 网络服务器中的连接对象:可以将连接对象的属性分为内部状态和外部状态,在连接对象被回收之前,可以复用已有的连接对象。
    • 实现过程:
      • 创建一个基于享元模式的图形编辑器,其中包含不同颜色的圆形,通过共享颜色相同的圆形对象来减少内存使用。
      • 1、创建享元接口和具体享元类,定义共享对象的操作方法,创建实现类实现接口并包含内部状态;
      • 2、创建享元工厂类,用于管理和共享享元对象。该工厂类维护一个flyweights字典来存储已创建的享元对象。
      • 3、客户端调用
    • 注意事项和最佳实践
      • 状态共享和线程安全
        • 在使用享元模式时,需要注意内部状态的共享和线程安全的问题。
        • 由于享元对象是被多个客户端共享的,所以需要保证内部状态的一致性。
      • 对象状态的外部管理:
        • 将对象的内部状态和外部状态分离,但外部状态需要由客户端来管理。
        • 客户端在使用享元对象时,需要将外部状态传递给享元对象进行操作。
      • 对象池的管理
        • 为了更好地管理和复用享元对象,可以使用对象池来存储已创建的享元对象。
        • 对象池可以提高对象的复用率,减少对象的创建和销毁开销。
  • 代理模式

    • 充当一个代理,控制某个对象的访问。客户端通过代理对象来访问目标对象,从而可以在目标对象的基础上增加额外的功能。
    • 代理模式是一种由两个或多个对象协同工作的设计模式。其中,一个对象是真正要调用的目标对象,而其他一个或多个对象充当代理对象。
    • 优点:
      • 代理对象可以处理一些公共的逻辑,例如对目标对象的访问控制、缓存、日志记录等。
      • 代理模式还可以实现懒汉式加载,即当需要访问目标对象时再进行实例化。
      • 可以在访问目标对象之前或之后执行一些附加操作。
    • 应用场景:
      • 远程代理:用于对网络上的对象进行本地化访问。
      • 虚拟代理:用于根据需要创建高开销的对象。
      • 安全代理:用于控制对对象的访问权限。
      • 智能引用:用于在访问对象时执行一些额外操作,例如对对象的计数等。
      • 应用于微服务架构中:可以使用代理来封装对其他微服务的访问,并在代理层实现负载均衡、限流、熔断等机制。
      • 代理模式还可以用于服务发现和路由功能的实现。
    • 实现过程:
      • 实现步骤1: 定义代理接口
      • 实现步骤2: 实现被代理对象
      • 实现步骤3: 实现代理对象
      • 实现步骤4: 调用代理对象
    • 代理模式和装饰器模式的区别
      • 都包含一个目标对象和一个代理/装饰器对象。
      • 代理模式一般是一个对目标对象的“访问控制”,而装饰器模式更关注对目标对象的“扩展”。
      • 代理模式通常在目标对象“之前或之后执行一些附加操作”,而装饰器模式则是在目标对象的基础上“动态添加额外的功能”。
    • 静态代理和动态代理的比较
      • 静态代理是在编译时就已经确定代理对象的类型,代理对象是由程序员手动编写的。
      • 动态代理则是在运行时动态生成代理对象,代理对象是由代理框架根据目标对象的接口自动生成的。相比而言,动态代理更加灵活,但也更加复杂。

行为型

  • 责任链模式

    • 它通过将请求的发送者和接收者解耦,使多个对象都有机会处理这个请求。每个接收者都包含对另一个接收者的引用,如果自己无法处理该请求,就会将请求转发给下一个接收者,直到请求被处理或者到达链的末尾。
    • 优点
      • 灵活性:可以动态地向责任链中添加、移除或者重新排序处理者,无需修改发送者和接收者的代码。
      • 解耦发送者和接收者:发送者无需关心请求是由哪个接收者处理,也不需要知道链中的具体处理者。
      • 可扩展性:容易扩展责任链,可以方便地添加新的具体处理者。
      • 单一职责原则:每个具体处理者都只需要关心自己的处理逻辑。
      • 可配置性:可以根据需要配置链中的处理者,使得不同的请求有不同的处理者链。
    • 在实际应用中有的场景:
      • Web应用中的请求处理:可以使用责任链模式来处理不同类型的请求,如身份验证、日志记录、权限验证等。
      • 错误处理:可以使用责任链模式来处理错误,每个处理者负责处理一种类型的错误,并根据需要将错误转发给下一个处理者。
      • 事件处理:可以使用责任链模式来处理不同类型的事件,如用户点击事件、网络请求事件等。
    • 实现过程:
      • 定义了一个抽象处理器(Handler)和两个具体处理器,客户端(Client)通过调用处理器的handleRequest方法来发起请求。
      • 实现步骤1: 定义抽象处理器接口,定义了处理请求的方法HandleRequest和设置下一个处理器的方法SetNext;
      • 实现步骤2: 实现具体处理器类,实现了抽象处理器接口,并重写了HandleRequest和SetNext方法;
      • 实现步骤3: 构建责任链,通过将具体处理器实例化并设置为下一个处理器,构建了一个责任链。
      • 实现步骤4: 客户端发起HandleRequest()请求
  • 命令模式

    • 将请求封装为一个对象,从而使你可以用不同的请求对客户端进行参数化。
    • 命令模式的主要目的是解耦发送者和接收者。通过将请求封装成一个对象,发送者只需要和命令对象进行交互,而不需要直接和接收者交互。
    • 优点
      • 将请求封装成对象,使发送者和接收者解耦。
      • 可以将请求排队、记录日志、撤销等操作。
      • 可以在不改变原有代码的情况下扩展新的命令。
    • 应用场景
      • 需要将请求和执行命令的对象解耦。
      • 需要支持撤销和重做操作。
      • 需要将一组操作排队执行。
    • 实现过程:
      • 有一个电视机作为接收者,可以执行打开和关闭的操作,也有一个遥控器作为调用者,它可以设置具体的命令并执行它。
      • 1、定义命令接口
      • 2、实现具体命令类
      • 3、定义接收器接口
      • 4、实现具体接收器类
      • 5、实现调用者角色
      • 6、客户端调用
    • 命令模式与策略模式的区别
      • 命令模式和策略模式在某种程度上是相似,它们都将某些行为封装成对象。
      • 不同之处在于,命令模式主要用于将请求“封装为对象”并实现“撤销和执行”队列等功能,
      • 策略模式则主要用于封装一系列的算法,并在运行时根据需要“选择其中一个”算法进行执行。
  • 迭代器模式

    • 提供一种统一的方法来遍历一个聚合对象中的各个元素,而无需暴露该聚合对象的内部表示。
    • 优点
      • 可以隐藏集合对象的内部结构,使得遍历算法和集合对象解耦。
      • 提供了一种统一的方式来遍历不同类型的集合对象。
    • 应用场景
      • 遍历一个数据库查询结果集。
      • 遍历一个集合中的元素。
      • 遍历一个文件系统中的文件和文件夹。
    • 实现过程:
      • 有两个主要的角色:Iterator(迭代器)和Collection(可迭代对象)
      • Collection对象创造Iterator对象并传入数据集合,这样就可以用迭代器的hasNext和next方法了;
      • 1、Iterator(迭代器)定义了遍历集合对象的接口,包括 hasNext() 方法用于判断是否还有下一个元素,以及 next() 方法用于获取下一个元素。
      • 2、ConcreteIterator(具体迭代器)是 Iterator(迭代器)的具体实现类,实现了 hasNext() 和 next() 方法。
      • 3、Collection(可迭代对象)定义了创建迭代器对象的接口 createIterator()。
      • 4、ConcreteCollection(具体可迭代对象)是 Collection(可迭代对象)的具体实现类,实现了 createIterator() 方法。
    • 可以使用生成器函数(yield)来简化迭代器的实现
      • 定义了一个 GenerateItems() 函数,它返回一个只读通道 (<-chan),在这个函数中使用 yield 将元素依次发送到通道中。
      • 在 main() 函数中使用 range 遍历这个只读通道,并输出元素值。
  • 中介者模式

    • 通过将对象之间的通信转移给一个中介者对象,从而减少对象之间的直接依赖关系。在中介者模式中各个对象不再直接与彼此通信,而是通过中介者对象进行通信。
    • 优点
      • 可以简化对象之间的通信,由中介者对象统一处理对象之间的通信。
      • 可以集中控制对象之间的交互,便于扩展和维护。
    • 应用场景
      • 一个机场调度系统中,调度员充当中介者角色,飞机、地面交通等各个模块作为同事类,通过调度员进行协调和通信。
    • 实现过程
      • 实现一个简单的聊天室应用,使用中介者模式来管理不同用户之间的通信。
      • 实现步骤1: 定义中介者接口和具体中介者
      • 实现步骤2: 定义同事类接口和具体同事类
      • 实现步骤3: 在中介者中管理同事类,在具体中介者类中,需要实现registerColleague方法和sendMessage方法来管理同事类之间的通信:
      • 实现步骤4: 客户端调用,创建了一个具体中介者对象和两个具体同事对象,然后通过中介者对象注册同事对象,并进行通信测试。
  • 备忘录模式

    • 用于保存和恢复对象的内部状态。它将对象的状态保存到备忘录对象中,以后可以通过备忘录对象将对象恢复到之前的状态。
    • 优点
      • 可以在不违反封装原则的情况下保存和恢复对象的内部状态。
      • 可以灵活地管理对象的历史状态,方便进行撤销和重做操作。
      • 可以将状态保存到外部,避免对象内部状态的暴露。
    • 应用场景
      • 文字编辑器中的撤销和重做功能,可以使用备忘录模式保存每次操作的状态。
      • 游戏中的保存和加载功能,可以使用备忘录模式保存游戏进度。
      • 电子邮件客户端中的草稿箱功能,可以使用备忘录模式保存草稿邮件的状态。
    • 实现步骤
      • Originator类,它具有一个内部状态state。Originator通过SetState方法设置状态,并通过CreateMemento方法创建备忘录对象。
      • 备忘录对象的内部状态与Originator的状态相同。
      • Caretaker类负责存储备忘录对象,并通过AddMemento方法添加备忘录对象。
      • 1、创建备忘录对象Memento,该对象具有保存Originator内部状态的方法GetState。
      • 2、创建Originator对象,该对象具有设置状态和创建备忘录的方法。
      • 3、创建Caretaker对象,该对象负责保存备忘录对象。
      • 4、在Originator中实现创建备忘录和恢复状态的方法:
        • 创建备忘录对象时,将Originator的状态保存到备忘录中。
        • 恢复状态时,将备忘录中的状态恢复到Originator中。
      • 5、在Caretaker中实现添加备忘录和获取备忘录的方法:
        • 添加备忘录方法用于将备忘录对象保存到Caretaker中。
        • 获取备忘录方法用于从Caretaker中获取备忘录对象。
  • 观察者模式

    • 用于在对象之间建立一对多的依赖关系。当一个对象(称为主题或者被观察者)发生改变时,它的所有依赖对象(称为观察者)都会得到通知并自动更新。
    • 优点
      • 被观察者和观察者之间是松耦合的,被观察者不需要知道观察者的具体实现细节。
      • 可以动态地添加和删除观察者,使系统更加灵活。
      • 主题和观察者之间遵循开闭原则,可以独立地进行扩展和重用。
      • 可以实现一对多的依赖关系,一个主题可以有多个观察者。
    • 应用场景
      • 电商平台中的促销活动通知。
      • GUI界面中的事件处理机制,如按钮被点击时的处理动作。
    • 实现步骤
      • 有一个主题(ConcreteSubject)注册了两个观察者(ObserverA和ObserverB),然后通知了这两个观察者
      • 1、创建主题(被观察者)接口和具体主题类,有三个方法Register、Remove、Notify
      • 2、创建观察者接口和具体观察者类,有一个方法update
      • 3、客户端调用
  • 状态模式

    • 用于解决对象在不同状态下有不同行为的问题。它将对象的行为封装到不同的状态类中,使得对象在运行时可以根据其内部状态的变化而改变行为。
    • 优点
      • 简化了条件语句的判断逻辑,提高了代码的可读性和可扩展性。
      • 将复杂的状态判断逻辑封装到不同的状态类中,增强了代码的可维护性。
      • 开闭原则:通过添加新的状态类,可以方便地增加新的状态。
      • 各个状态类之间相互独立,修改一个状态类不会影响其他状态类的代码。
    • 应用场景
      • 交通信号灯:根据不同的状态,交通信号灯会发出不同的光信号。
      • 订单状态管理:订单在不同的状态下有不同的操作和行为,如已支付、已发货、已签收等。
    • 实现步骤
      • 实现一个简单的订单状态管理系统。订单有多个状态,如已支付、已发货、已签收等。根据不同的状态,订单有不同的操作和行为。
      • 1、定义订单状态接口和具体状态类,实现Handle切换状态;
      • 2、定义订单上下文类和状态切换方法,Context中传入state状态类;
      • 3、实现具体状态类的切换方法
      • 4、使用状态模式管理订单状态
  • 策略模式

    • 根据不同的情况选择不同的算法或行为。它将不同的算法封装在独立的策略类中,并使这些策略类可以互相替换。可以在运行时动态地改变一个对象的行为;
    • 优点
      • 策略类可以独立变化,增加新的策略类对原有代码没有影响,符合开闭原则。
      • 客户端可以根据需要选择不同的策略,符合单一职责原则。
      • 策略模式提供了可重用的算法或行为,可以避免代码重复。
    • 应用场景
      • 不同的支付方式,例如支付宝、微信支付等。
      • 不同的排序算法,例如冒泡排序、快速排序等。
      • 不同的日志记录方式,例如控制台输出、文件输出等。
    • 实现步骤
      • 策略模式的三个角色:Strategy接口、具体策略类(例如ConcreteStrategyA和ConcreteStrategyB)以及上下文类Context。
      • 以电商系统订单支付方式的选择为例。客户端根据不同的支付方式选择对应的策略(ConcreteStrategyA或ConcreteStrategyB),然后调用上下文类的方法进行支付。
      • 实现步骤1: 定义策略接口和具体策略类。定义一个策略接口Strategy,其中包含一个Execute(data interface{}) string方法用于执行具体的策略。
      • 实现步骤2: 实现上下文类。实现上下文类Context,该类封装了具体的策略对象,并提供SetStrategy(strategy Strategy)方法用于设置策略对象,以及ExecuteStrategy(data interface{}) string方法用于执行具体的策略。
      • 实现步骤3: 使用策略模式完成实际业务逻辑
      • 实现步骤4: 根据请求参数,通过SetStrategy方法设置具体策略;
  • 模板方法模式

    • 定义了一个算法的骨架,将一些步骤推迟到子类中实现。这样可以在不改变算法结构的情况下,允许子类在特定步骤上具体实现自己的行为。
    • 优点
      • 定义了一个算法的骨架,将一些特定步骤的具体实现延迟到子类中。
      • 可以减少代码的重复,提高代码的复用性。
    • 应用场景
      • 框架中的钩子方法:钩子方法可以在算法的某些步骤中提供一个扩展点,允许子类根据需要进行扩展。
      • 数据库操作中的模板方法:例如在数据库操作中,可以将连接、打开事务、执行SQL语句、关闭连接等行为抽象为模板方法,由子类实现具体操作。
      • GUI应用程序中的事件处理:GUI应用程序中的事件处理通常具有一定的规则和流程,可以使用模板方法模式实现。
    • 实现步骤
      • 一个抽象类 AbstractClass,其中包含了一个模板方法 TemplateMethod 和一些抽象方法 AbstractMethod1 和 AbstractMethod2。
      • 具体的子类 ConcreteClass1 和 ConcreteClass2 继承自 AbstractClass,实现了具体的抽象方法。
      • 实现步骤1:定义抽象模板类。定义一个抽象模板类 AbstractClass,在该类中声明模板方法和抽象方法。
      • 实现步骤2:实现具体模板类。创建具体的模板类,例如 ConcreteClass1 和 ConcreteClass2,在这些类中实现抽象方法。
      • 实现步骤3:定义抽象方法和具体方法。在抽象模板类中,需要定义一些抽象方法和具体方法。抽象方法由具体子类去实现,具体方法在模板方法中被调用。
      • 实现步骤4:在客户端代码中使用模板方法,实现具体的业务逻辑。
  • 访问者模式

    • 将数据结构与数据操作分离,实现对数据的不同操作而不改变数据结构,使得操作更加灵活和可扩展。
    • 优点
      • 访问者模式可以将数据结构与操作解耦,从而使操作更加灵活和可扩展。
      • 添加新的操作非常方便,无需修改已有代码。
      • 增加新的操作非常方便,符合开闭原则。
      • 可以对数据结构进行复杂的操作,而无需改变数据结构本身。
    • 应用场景
      • 编译器的语法树分析阶段,可以使用访问者模式来实现不同的语法检查和代码转换操作。
      • 数据库查询优化器中,可以使用访问者模式来实现对查询树的不同优化操作。
    • 实现步骤
      • 使用接口和函数的组合来实现动态绑定,从而达到解耦的目的
      • 访问者模式中包含以下角色:
        • Element 定义了一个接受访问者的接口方法 Accept。
        • ConcreteElementA 和 ConcreteElementB 是具体的元素类,实现了 Accept 方法,并定义了自己的操作方法。
        • Visitor 是访问者接口,定义了访问具体元素的方法。
        • ConcreteVisitor1 和 ConcreteVisitor2 是具体的访问者类,实现了访问具体元素的方法。
      • 实现步骤1: 定义访问者接口和具体访问者类。
      • 实现步骤2: 定义元素接口和具体元素类。
      • 实现步骤3: 定义对象结构和具体对象结构。
      • 实现步骤4: 在对象结构中实现元素的访问接口,将访问操作委托给访问者。
      • 实现步骤5: 定义客户端代码来使用访问者模式。

名词

  • UML全称为Unified Modeling Language,即统一建模语言;
  • UML是一种面向对象的用来设计软件蓝图的可视化建模语言,它支持面向对象系统的分析、设计、实现和交付等各个环节

laravel中用到的设计模式

  • 观察者模式(Observer):Laravel 中的事件系统就是基于观察者模式实现的;
  • 单例(Singleton):Laravel 单例模式用于管理应用程序的一些核心组件,如数据库连接、事件调度器等。
  • 策略(Strategy):Laravel 中的策略模式允许您对不同的用户或应用程序状态应用不同的逻辑。
  • 模板方法(Template Method):Laravel 中的一些基类(如控制器和模型)使用了模板方法模式,定义了一个操作的骨架,具体实现由子类提供。
  • 迭代器(Iterator):Laravel 中的集合和查询构建器实现了迭代器模式,允许您遍历和操作集合中的数据。
  • 适配器(Adapter):Laravel 中的缓存适配器和邮件驱动等组件使用适配器模式,使不同的实现可以相互替换。
  • 简单工厂(Simple Factory):Laravel 中的简单工厂模式用于创建各种驱动和服务的实例,简化了实例化的过程。
  • 中介者(Mediator):Laravel 的事件系统使用了中介者模式,允许不同的对象通过事件与中介者进行通信,而不会直接依赖于彼此。
  • 委托(Delegation):Laravel 的验证器(Validator)使用了委托模式,将特定的验证规则委托给各个验证器类来处理,提供了可扩展的验证机制。
  • 过滤器(Filter):Laravel 的路由系统使用了过滤器模式,允许开发人员在请求到达控制器之前或之后执行一系列的过滤操作,方便实现权限控制、日志记录等功能。
  • 享元(Flyweight):Laravel 的数据库查询构建器(Query Builder)使用了享元模式,通过共享查询构建器实例,提供了更高效的数据库查询操作。
  • 命令(Command):Laravel 命令行工具 Artisan 使用命令模式,每个命令都是一个独立的对象,用于执行特定的任务。
  • 责任链(Chain of Responsibility):Laravel 的异常处理器(Exception Handler)使用了责任链模式,允许开发人员定义和组织多个异常处理器,并按顺序处理异常。
  • 装饰器(Decorator):Laravel 的中间件(Middleware)使用了装饰器模式,允许开发人员在请求处理前后添加附加的逻辑处理。

laravel和thinkphp的区别

  • laravel底层源码用了更多的设计模式,thinkphp简单的多,
  • laravel的生命周期更为复杂,thinkphp加载了几个文件,就分发了请求到控制器,每次执行请求的速度会慢一些;
  • laravel属于中量级级别的,thinkphp属于轻量级别的。
  • 一个典型的Laravel应用的生命周期如下:
    • 1、解析HTTP请求请求的URL,根据定义的路由规则找到匹配的路由。
    • 2、在路由执行之前,会经过中间件处理(身份验证、权限控制、请求过滤等)。
    • 3、路由匹配后,会调用相应的控制器方法;
    • 4、响应返回:完成整个请求-响应周期。
    • Laravel的复杂生命周期带来了更多的灵活性和扩展性;
    • 使用中间件、事件、服务提供者等功能来处理各种复杂的业务逻辑;
  • ThinkPHP的生命周期相对简单,主要包括
    • 路由解析、控制器处理、模型操作和视图渲染等,这不跟laravel一样吗。
    • ThinkPHP采用了简单直接的设计思路,易于上手和理解。

PHP与Go的区别

  • PHP

    • 用途:PHP 最初是设计用于 Web 开发,特别是用于服务器端脚本编写。它主要用于构建动态网页和网站。
    • 语法:PHP 是一种脚本语言,类 C 语言的语法风格,易于学习和上手。
    • 类型系统:PHP 是一种弱类型语言,变量在运行时可以动态地更改类型,这可能导致一些意外行为。
    • 运行环境:PHP 是解释执行的,通常与 Web 服务器(比如 Apache、Nginx)结合使用,通过 PHP 解释器执行代码。
    • 并发性:PHP 最初并不擅长处理高并发,但随着版本更新,性能有所改善。在处理大量并发请求时可能需要额外的配置或依赖。
    • 生态系统:PHP 拥有庞大的生态系统,有很多第三方库和框架,如 Laravel、Symfony 等,用于简化开发和提高效率。
  • Go

    • 用途:Go 是一种通用编程语言,旨在简化并发编程。它可以用于系统编程、网络编程、云计算等领域。
    • 语法:Go 的语法相对简单,但功能强大。它更注重效率和性能,同时也很容易学习。
    • 类型系统:Go 是一种静态类型语言,变量在编译时就确定了其类型,这有助于提高代码的稳定性和可靠性。
    • 运行环境:Go 是一种编译型语言,它的代码需要先编译成二进制文件,然后才能运行。它可以独立运行,无需其他运行时环境。
    • 并发性:Go 在设计之初就内置了原生支持并发的机制(goroutines 和 channels),因此在高并发环境下表现出色。
    • 性能:Go 因其并发性和编译型特性而被广泛用于构建高性能的应用程序,尤其在网络服务和分布式系统方面表现出色。