Golang 设计模式(行为型)

devtools/2024/9/24 10:17:33/

文章目录

  • 策略模式
  • 迭代器模式
  • 访问者模式
  • 观察者模式
  • 命令模式
  • 模板方法模式
  • 责任链模式
  • 状态模式

策略模式

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成独立的对象,使得它们可以互相替换。在 Go 语言中,策略模式可以被广泛应用,并且具有许多优点和适用场景。

  • 场景

    1. 算法的选择需要在运行时动态确定: 当需要根据不同的条件或环境动态地选择算法时,策略模式非常有用。例如,根据用户的权限级别选择不同的身份验证策略。
    2. 需要避免使用大量的条件语句: 如果系统中存在大量的条件语句,并且它们在不同的情况下执行不同的操作,可以考虑使用策略模式来替换这些条件语句,使得代码更加清晰和易于维护。
    3. 需要实现同一种算法的多个变体: 当需要实现同一种算法的多个变体时,例如不同的排序算法或不同的文件压缩算法,策略模式可以使得这些变体可以被轻松地替换和切换。
  • 优点

    1. 灵活性: 策略模式允许在运行时动态地选择算法,而不需要修改客户端代码。这使得系统更加灵活,可以根据需要更改或添加新的算法。
    2. 可维护性: 每个算法都被封装在自己的类中,使得它们易于理解、维护和测试。当需要修改或添加新的算法时,不会影响到其他算法或客户端代码。
    3. 复用性: 策略模式提供了一种将算法独立于上下文而重用的方式。多个上下文可以共享相同的策略,从而避免了重复编码和冗余。
    4. 单一职责原则: 策略模式遵循单一职责原则,每个策略都专注于执行一个特定的任务,使得代码更加清晰、简洁和可维护。
  • 缺点

    1. 类爆炸: 如果系统中有大量的算法,并且每个算法都需要一个单独的类来实现,可能会导致类爆炸问题,使得代码结构复杂化。
    2. 客户端必须了解所有策略: 客户端必须了解所有可用的策略,以便选择合适的策略。这可能会增加客户端代码的复杂性。
  • 示例

package mainimport "fmt"// PaymentStrategy 支付策略接口
type PaymentStrategy interface {Pay(amount float64)
}// CreditCardStrategy 信用卡支付策略
type CreditCardStrategy struct{}func (c *CreditCardStrategy) Pay(amount float64) {fmt.Printf("使用信用卡支付:%.2f 元\n", amount)
}// CashStrategy 现金支付策略
type CashStrategy struct{}func (c *CashStrategy) Pay(amount float64) {fmt.Printf("使用现金支付:%.2f 元\n", amount)
}// PaymentContext 支付上下文
type PaymentContext struct {strategy PaymentStrategy
}func (p *PaymentContext) SetStrategy(strategy PaymentStrategy) {p.strategy = strategy
}func (p *PaymentContext) Pay(amount float64) {p.strategy.Pay(amount)
}func main() {// 创建支付上下文context := &PaymentContext{}// 设置信用卡支付策略并支付context.SetStrategy(&CreditCardStrategy{})context.Pay(100.50)// 设置现金支付策略并支付context.SetStrategy(&CashStrategy{})context.Pay(50.25)
}
  • 输出结果
bash">使用信用卡支付:100.50 元
使用现金支付:50.25 元

迭代器模式

迭代器模式是一种行为型设计模式,它允许顺序访问一个聚合对象中的各个元素,而不暴露该对象的内部表示。通过迭代器模式,可以在不知道聚合对象内部结构的情况下,遍历聚合对象中的元素。

  1. 迭代器(Iterator): 定义了访问和遍历聚合对象元素的接口,包括获取下一个元素、判断是否还有元素等方法。
  2. 具体迭代器(ConcreteIterator): 实现了迭代器接口,负责遍历具体的聚合对象。
  3. 聚合对象(Aggregate): 定义了创建迭代器的接口,可以是一个集合或者容器。
  4. 具体聚合对象(ConcreteAggregate): 实现了聚合对象接口,负责创建具体的迭代器。
  • 场景

    1. 遍历聚合对象: 当需要遍历一个聚合对象中的元素,并且不希望暴露其内部表示时,可以使用迭代器模式。
    2. 统一访问方式: 当需要对不同类型的聚合对象提供统一的访问方式时,可以使用迭代器模式。通过迭代器模式,可以将不同类型的聚合对象统一成具有相同的接口。
    3. 支持多种遍历方式: 当需要支持多种不同的遍历方式时,可以使用迭代器模式。通过定义不同的迭代器,可以实现不同的遍历方式,如顺序遍历、逆序遍历等。
  • 优点

    1. 简化客户端代码: 迭代器模式将遍历聚合对象的逻辑封装在迭代器中,使得客户端不需要直接操作聚合对象,简化了客户端代码。
    2. 支持多种遍历方式: 迭代器模式可以定义多种不同的迭代器,支持不同的遍历方式,提高了灵活性。
    3. 解耦聚合对象和迭代算法: 迭代器模式将聚合对象和迭代算法解耦,使得它们可以独立变化,符合单一职责原则。
  • 缺点

    1. 增加类的个数: 迭代器模式会增加系统中类的个数,导致系统变得更加复杂。
    2. 不适合对聚合对象频繁修改: 当聚合对象频繁修改时,可能需要频繁修改迭代器,影响系统的稳定性。
  • 示例

package mainimport "fmt"// Iterator 迭代器接口
type Iterator interface {HasNext() boolNext() interface{}
}// Aggregate 聚合接口
type Aggregate interface {CreateIterator() Iterator
}// ConcreteIterator 具体迭代器
type ConcreteIterator struct {index intdata  []interface{}
}func NewConcreteIterator(data []interface{}) *ConcreteIterator {return &ConcreteIterator{index: 0,data:  data,}
}func (it *ConcreteIterator) HasNext() bool {return it.index < len(it.data)
}func (it *ConcreteIterator) Next() interface{} {if it.HasNext() {val := it.data[it.index]it.index++return val}return nil
}// ConcreteAggregate 具体聚合
type ConcreteAggregate struct {data []interface{}
}func NewConcreteAggregate() *ConcreteAggregate {return &ConcreteAggregate{data: make([]interface{}, 0),}
}func (a *ConcreteAggregate) Add(item interface{}) {a.data = append(a.data, item)
}func (a *ConcreteAggregate) CreateIterator() Iterator {return NewConcreteIterator(a.data)
}func main() {// 创建具体聚合aggregate := NewConcreteAggregate()// 添加元素aggregate.Add("元素A")aggregate.Add("元素B")aggregate.Add("元素C")// 创建具体迭代器iterator := aggregate.CreateIterator()// 遍历元素并打印中文内容for iterator.HasNext() {val := iterator.Next()fmt.Println("当前元素:", val)}
}
  • 输出结果
bash">当前元素: 元素A
当前元素: 元素B
当前元素: 元素C

访问者模式

访问者模式是一种行为型设计模式,它允许你对一组对象的元素应用一些操作,而不暴露这些对象的内部结构。访问者模式将数据结构与数据操作分离开来,使得数据结构可以在不修改的情况下增加新的操作。

  1. 访问者(Visitor): 定义了对数据结构中各元素进行操作的方法。通过访问者模式,可以在不修改元素类的情况下添加新的操作。
  2. 元素(Element): 表示数据结构中的各个元素,通常包含一个接受访问者的方法。
  3. 具体访问者(ConcreteVisitor): 实现了访问者接口中定义的操作,对具体的元素进行具体的操作。
  4. 具体元素(ConcreteElement): 实现了元素接口中的方法,通常是访问者访问的对象。
  • 场景

    1. 数据结构稳定但操作多变: 当数据结构相对稳定,但需要经常添加新的操作时,可以使用访问者模式。访问者模式将操作封装在访问者中,从而避免了对数据结构的修改。
    2. 数据结构复杂且操作频繁变化: 当数据结构非常复杂,且操作频繁变化时,可以使用访问者模式。通过访问者模式,可以将数据结构与操作解耦,使得操作可以灵活地组合和修改。
    3. 数据结构多样化且操作分散: 当数据结构非常多样化,且各种操作分散在不同的地方时,可以使用访问者模式。访问者模式将操作统一封装在访问者中,从而避免了操作的分散性。
  • 优点

    1. 增加新操作方便: 可以通过添加新的访问者来增加新的操作,而不需要修改元素类。
    2. 提高数据结构的稳定性: 可以将不同的操作封装在访问者中,从而使得数据结构相对稳定,不容易受到外部操作的影响。
    3. 符合开闭原则: 访问者模式将操作和数据结构分离开来,使得操作可以独立地扩展,符合开闭原则。
  • 缺点

    1. 增加新元素困难: 当需要添加新的元素时,需要修改所有的访问者类,这可能会导致代码的维护困难。
    2. 破坏封装性: 访问者模式可能会破坏数据结构的封装性,因为访问者需要访问数据结构中的各个元素,可能会暴露数据结构的内部细节。
  • 示例

package mainimport "fmt"// Element 元素接口
type Element interface {Accept(visitor Visitor)
}// ConcreteElementA 具体元素A
type ConcreteElementA struct{}func (e *ConcreteElementA) Accept(visitor Visitor) {visitor.VisitConcreteElementA(e)
}// ConcreteElementB 具体元素B
type ConcreteElementB struct{}func (e *ConcreteElementB) Accept(visitor Visitor) {visitor.VisitConcreteElementB(e)
}// Visitor 访问者接口
type Visitor interface {VisitConcreteElementA(element *ConcreteElementA)VisitConcreteElementB(element *ConcreteElementB)
}// ConcreteVisitor 具体访问者
type ConcreteVisitor struct{}func (v *ConcreteVisitor) VisitConcreteElementA(element *ConcreteElementA) {fmt.Println("访问者访问具体元素A")
}func (v *ConcreteVisitor) VisitConcreteElementB(element *ConcreteElementB) {fmt.Println("访问者访问具体元素B")
}func main() {// 创建具体元素elementA := &ConcreteElementA{}elementB := &ConcreteElementB{}// 创建具体访问者visitor := &ConcreteVisitor{}// 元素接受访问者访问elementA.Accept(visitor)elementB.Accept(visitor)
}
  • 输出结果
bash">访问者访问具体元素A
访问者访问具体元素B

观察者模式

观察者模式是一种行为型设计模式,它定义了一种一对多的依赖关系,当一个对象的状态发生改变时,其所有依赖者都会收到通知并自动更新。在 Go 中,观察者模式通常通过接口和回调函数来实现。

  • 场景

    1. GUI 开发: 在 GUI 开发中,观察者模式常用于实现用户界面和数据模型之间的同步,例如当数据模型发生变化时,通知界面更新。
    2. 事件处理: 观察者模式适用于事件驱动的系统中,被观察者可以是事件源,而观察者可以是事件处理器,当事件发生时通知观察者处理事件。
    3. 发布-订阅系统: 观察者模式也被广泛应用于发布-订阅系统中,其中被观察者充当发布者,而观察者充当订阅者,当发布者发布新消息时,通知所有订阅者。
    4. 消息队列系统: 观察者模式可以用于消息队列系统中,其中被观察者为消息队列,观察者为消息消费者,当消息队列中有新消息时,通知所有的消费者进行处理。
  • 优点

    1. 松耦合: 观察者模式将被观察者和观察者解耦,使得它们可以独立地进行修改和扩展,降低了对象之间的耦合度。
    2. 可扩展性: 由于观察者模式中被观察者和观察者之间的关系是松散的,因此可以方便地增加新的观察者或被观察者,而不会对现有代码产生影响。
    3. 通知机制: 观察者模式提供了一种灵活的通知机制,使得观察者可以及时获知被观察者的状态变化,从而采取相应的行动。
  • 缺点

    1. 过多的通知: 当被观察者的状态频繁变化时,会产生大量的通知,可能会导致性能问题。
    2. 循环依赖: 如果观察者之间存在循环依赖,可能会导致系统混乱和不稳定。
  • 示例

package mainimport "fmt"// Observer 观察者接口
type Observer interface {Update(message string)
}// Subject 主题接口
type Subject interface {RegisterObserver(observer Observer)RemoveObserver(observer Observer)NotifyObservers()
}// ConcreteObserver 具体观察者
type ConcreteObserver struct {name string
}func (o *ConcreteObserver) Update(message string) {fmt.Printf("观察者 %s 收到通知:%s\n", o.name, message)
}// ConcreteSubject 具体主题
type ConcreteSubject struct {observers []Observer
}func (s *ConcreteSubject) RegisterObserver(observer Observer) {s.observers = append(s.observers, observer)
}func (s *ConcreteSubject) RemoveObserver(observer Observer) {for i, obs := range s.observers {if obs == observer {s.observers = append(s.observers[:i], s.observers[i+1:]...)break}}
}func (s *ConcreteSubject) NotifyObservers() {message := "有新消息"for _, observer := range s.observers {observer.Update(message)}
}func main() {// 创建具体主题subject := &ConcreteSubject{}// 创建具体观察者observerA := &ConcreteObserver{name: "观察者A"}observerB := &ConcreteObserver{name: "观察者B"}// 注册观察者subject.RegisterObserver(observerA)subject.RegisterObserver(observerB)// 发送通知给观察者subject.NotifyObservers()// 移除观察者Asubject.RemoveObserver(observerA)// 再次发送通知给观察者subject.NotifyObservers()
}
  • 输出结果
bash">观察者 观察者A 收到通知:有新消息
观察者 观察者B 收到通知:有新消息
观察者 观察者B 收到通知:有新消息

命令模式

命令模式是一种行为型设计模式,它将请求封装成一个对象,从而使得可以用不同的请求对客户端进行参数化,并且能够支持请求的排队、记录请求日志、撤销操作等。在 Go 中,命令模式通常通过定义接口和实现类来实现。

  • 场景

    1. 菜单和工具栏操作: 命令模式可以用于实现菜单和工具栏中的各种操作,例如点击按钮执行某些操作,或者选择菜单项执行特定的命令。
    2. 撤销和重做功能: 命令模式可以用于实现撤销和重做功能,每个命令对象保存执行操作所需的所有信息,可以轻松地撤销和重做操作。
    3. 任务调度系统: 命令模式可以用于实现任务调度系统,其中命令对象表示不同的任务,调度器负责执行这些任务,并且可以对任务进行排队、调度和撤销。
    4. 日志记录系统: 命令模式可以用于实现日志记录系统,每个命令对象表示一个日志记录操作,可以保存日志记录的相关信息,并且可以将日志记录到文件或数据库中。
  • 优点

    1. 解耦请求发送者和接收者: 命令模式将请求封装成一个对象,使得请求发送者和接收者之间解耦,可以在不影响客户端的情况下修改请求的处理逻辑。
    2. 可扩展性: 新的命令可以很容易地添加到系统中,无需修改现有的客户端代码。
    3. 支持撤销和重做操作: 命令对象可以保存执行操作所需的所有信息,从而支持撤销和重做操作。
  • 缺点

    1. 类爆炸: 当命令类的数量增加时,可能会导致类的数量急剧增加,从而增加了系统的复杂性。
    2. 内存消耗: 由于每个命令对象都需要保存执行操作所需的所有信息,可能会占用大量的内存。
  • 示例

package mainimport "fmt"// Command 命令接口
type Command interface {Execute()
}// Receiver 接收者
type Receiver struct{}func (r *Receiver) Action1() {fmt.Println("接收者执行操作1")
}func (r *Receiver) Action2() {fmt.Println("接收者执行操作2")
}// ConcreteCommand1 具体命令1
type ConcreteCommand1 struct {receiver *Receiver
}func NewConcreteCommand1(receiver *Receiver) *ConcreteCommand1 {return &ConcreteCommand1{receiver: receiver}
}func (c *ConcreteCommand1) Execute() {c.receiver.Action1()
}// ConcreteCommand2 具体命令2
type ConcreteCommand2 struct {receiver *Receiver
}func NewConcreteCommand2(receiver *Receiver) *ConcreteCommand2 {return &ConcreteCommand2{receiver: receiver}
}func (c *ConcreteCommand2) Execute() {c.receiver.Action2()
}// Invoker 调用者
type Invoker struct {commands []Command
}func (i *Invoker) SetCommand(command Command) {i.commands = append(i.commands, command)
}func (i *Invoker) ExecuteCommands() {for _, command := range i.commands {command.Execute()}
}func main() {// 创建接收者receiver := &Receiver{}// 创建具体命令1和命令2并传入接收者command1 := NewConcreteCommand1(receiver)command2 := NewConcreteCommand2(receiver)// 创建调用者并设置命令invoker := &Invoker{}invoker.SetCommand(command1)invoker.SetCommand(command2)// 执行命令invoker.ExecuteCommands()
}
  • 输出结果
bash">接收者执行操作1
接收者执行操作2

模板方法模式

模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并允许子类重写算法的特定步骤,而不改变算法的结构。模板方法模式通过将算法的通用步骤放在父类中,而将特定步骤的实现延迟到子类中,从而提高了代码的复用性和扩展性。

  • 场景

    1. 实现算法的共享: 当有多个相关的类需要实现相似的算法时,可以使用模板方法模式将算法的通用步骤放在父类中,避免代码重复。
    2. 框架设计: 模板方法模式常用于框架设计中,框架提供了一个算法的骨架,而具体的业务逻辑由子类来实现。
    3. 定义标准化接口: 模板方法模式可以定义一个标准化的接口,每个子类根据自己的需求实现接口的具体步骤,从而实现了接口的标准化。
  • 优点

    1. 代码复用: 模板方法模式将算法的通用步骤放在父类中,可以在子类中重用这些通用步骤,减少了代码的重复。
    2. 提高扩展性: 子类可以通过重写特定步骤的实现来改变算法的行为,从而提高了系统的灵活性和可扩展性。
    3. 封装不变部分: 模板方法模式将算法的不变部分封装在父类中,将可变部分交给子类来实现,降低了系统的耦合度。
  • 缺点

    1. 增加了类的数量: 模板方法模式会增加系统中类的数量,每个具体子类都需要实现特定的步骤,可能会导致类的数量急剧增加,增加了系统的复杂性。
    2. 不便于子类扩展: 如果算法的结构在父类中被固定,可能会限制子类对算法的扩展,不够灵活。
  • 示例

package mainimport "fmt"// AbstractClass 抽象类
type AbstractClass interface {TemplateMethod()PrimitiveOperation1()PrimitiveOperation2()
}// ConcreteClass 具体类
type ConcreteClass struct{}func (c *ConcreteClass) TemplateMethod() {fmt.Println("调用模板方法")c.PrimitiveOperation1()c.PrimitiveOperation2()
}func (c *ConcreteClass) PrimitiveOperation1() {fmt.Println("具体方法1的实现")
}func (c *ConcreteClass) PrimitiveOperation2() {fmt.Println("具体方法2的实现")
}func main() {// 创建具体类对象concreteClass := &ConcreteClass{}// 调用模板方法concreteClass.TemplateMethod()
}
  • 输出结果
bash">调用模板方法
具体方法1的实现
具体方法2的实现

责任链模式

责任链模式是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免了发送者和接收者之间的耦合关系。在责任链模式中,请求沿着责任链传递,直到有一个对象处理请求为止。如果没有任何对象处理请求,请求将被忽略或者返回一个默认值。

  • 场景

    1. 请求的处理顺序不确定: 当请求的处理顺序不确定或者需要动态地确定时,可以使用责任链模式。例如,一个请求可能需要先经过权限验证,然后再经过日志记录,最后再进行业务处理。
    2. 解耦请求发送者和接收者: 当请求发送者和接收者之间的耦合关系较强,需要解耦时,可以使用责任链模式。例如,一个请求发送者可能不知道具体的接收者,而是将请求发送给责任链,直到有一个接收者处理请求为止。
    3. 多级审批流程: 责任链模式常用于多级审批流程中,每个处理器表示一个审批者,请求沿着责任链传递,直到有一个审批者处理请求为止。
  • 优点

    1. 降低耦合度: 责任链模式允许请求者和接收者之间的解耦,因为请求者不需要知道具体的接收者,而是将请求沿着责任链传递,直到有一个接收者处理请求。
    2. 灵活性和可扩展性: 责任链模式可以动态地组织处理器之间的关系,可以灵活地增加、删除或者修改处理器,从而提高了系统的灵活性和可扩展性。
    3. 单一职责原则: 责任链模式将请求的处理逻辑封装在各个处理器中,每个处理器只负责处理特定的请求,符合单一职责原则。
  • 缺点

    1. 可能导致请求被忽略: 如果责任链没有正确地设置,或者没有一个处理器能够处理请求,请求可能会被忽略或者返回一个默认值,这可能会导致系统的不稳定。
    2. 性能问题: 如果责任链过长或者处理器的数量过多,可能会导致性能问题,因为请求需要在责任链中传递多次,直到找到合适的处理器为止。
  • 示例

package mainimport "fmt"// Request 请求结构体
type Request struct {content string // 请求内容
}// Handler 处理器接口
type Handler interface {HandleRequest(request *Request)SetSuccessor(successor Handler)
}// ConcreteHandler 具体处理器
type ConcreteHandler struct {name      string  // 处理器名称successor Handler // 后继处理器
}func (c *ConcreteHandler) HandleRequest(request *Request) {if c.canHandle(request) {fmt.Printf("%s 处理请求:%s\n", c.name, request.content)} else if c.successor != nil {fmt.Printf("%s 无法处理请求,交由 %s 处理\n", c.name, c.successor.(*ConcreteHandler).name)c.successor.HandleRequest(request)} else {fmt.Printf("%s 无法处理请求\n", c.name)}
}func (c *ConcreteHandler) canHandle(request *Request) bool {// 检查处理器是否能够处理请求的逻辑// 这里简单地假设处理器名称包含请求内容的第一个字符即可处理return c.name[0] == request.content[0]
}func (c *ConcreteHandler) SetSuccessor(successor Handler) {c.successor = successor
}func main() {// 创建具体处理器handlerA := &ConcreteHandler{name: "处理器A"}handlerB := &ConcreteHandler{name: "处理器B"}handlerC := &ConcreteHandler{name: "处理器C"}// 设置责任链关系handlerA.SetSuccessor(handlerB)handlerB.SetSuccessor(handlerC)// 发送请求给责任链request1 := &Request{content: "apple"}request2 := &Request{content: "banana"}request3 := &Request{content: "carrot"}handlerA.HandleRequest(request1)handlerA.HandleRequest(request2)handlerA.HandleRequest(request3)
}
  • 输出结果
bash">处理器A 无法处理请求,交由 处理器B 处理
处理器B 无法处理请求,交由 处理器C 处理
处理器C 无法处理请求
处理器A 无法处理请求,交由 处理器B 处理
处理器B 无法处理请求,交由 处理器C 处理
处理器C 无法处理请求
处理器A 无法处理请求,交由 处理器B 处理
处理器B 无法处理请求,交由 处理器C 处理
处理器C 无法处理请求

状态模式

状态模式是一种行为型设计模式,它允许对象在内部状态发生改变时改变其行为。状态模式将对象的行为包装在不同的状态对象中,并且允许对象在运行时根据内部状态的改变而改变其行为。这种模式使得对象在不同的状态下可以有不同的行为,而且可以动态地切换状态。

  • 场景

    1. 对象的行为取决于其状态: 当一个对象的行为取决于其内部状态,并且在运行时可以动态地改变其状态时,可以使用状态模式。例如,电梯的行为取决于其当前状态(停止、运行、故障等)。
    2. 状态之间存在转换: 当对象的状态之间存在转换,并且每个状态都有不同的行为时,可以使用状态模式。例如,交通信号灯根据不同的状态(红灯、绿灯、黄灯)展示不同的行为。
    3. 避免使用大量的条件语句: 当需要根据对象的状态执行不同的行为时,通常会使用大量的条件语句来实现,此时可以考虑使用状态模式来替换这些条件语句,使得代码更加清晰和易于维护。
  • 优点

    1. 简化条件逻辑: 状态模式通过将状态和状态相关的行为封装在状态对象中,可以简化复杂的条件逻辑,使代码更加清晰易懂。
    2. 方便扩展: 当需要增加新的状态时,只需要创建一个新的状态对象并且在上下文中进行注册,不需要修改现有的代码,符合开闭原则。
    3. 提高代码复用性: 状态模式将状态相关的行为封装在状态对象中,可以在不同的上下文中重用相同的状态对象,减少了代码的重复。
    4. 符合单一职责原则: 每个状态都封装了一组相关的行为,使得每个状态对象都具有清晰的职责,符合单一职责原则。
  • 缺点

    1. 增加类的数量: 状态模式会增加系统中类的数量,每个状态都需要一个对应的状态类,可能会导致类的数量急剧增加,增加了系统的复杂性。
    2. 状态切换的开销: 状态模式将状态和状态相关的行为封装在不同的状态对象中,当状态切换时可能会导致对象的状态转换开销增加,特别是当有大量的状态和状态转换时。
  • 示例

package mainimport "fmt"// State 状态接口
type State interface {Handle(context *Context)
}// Context 上下文
type Context struct {state State
}func (c *Context) SetState(state State) {c.state = state
}func (c *Context) Request() {c.state.Handle(c)
}// ConcreteStateA 具体状态A
type ConcreteStateA struct{}func (s *ConcreteStateA) Handle(context *Context) {fmt.Println("处理状态A")context.SetState(&ConcreteStateB{})
}// ConcreteStateB 具体状态B
type ConcreteStateB struct{}func (s *ConcreteStateB) Handle(context *Context) {fmt.Println("处理状态B")context.SetState(&ConcreteStateA{})
}func main() {// 创建上下文,并设置初始状态为状态Acontext := &Context{state: &ConcreteStateA{}}// 不断发出请求,观察状态的变化context.Request()context.Request()context.Request()context.Request()
}
  • 输出结果
bash">处理状态A
处理状态B
处理状态A
处理状态B

http://www.ppmy.cn/devtools/31852.html

相关文章

第十五届蓝桥杯省赛大学B组(c++)

很幸运拿了辽宁赛区的省一,进入6月1号的国赛啦... 这篇文章主要对第十五届省赛大学B组(C)进行一次完整的复盘,这次省赛2道填空题6道编程题: A.握手问题 把握手情景看成矩阵: 粉色部分是7个不能互相捂手的情况 由于每个人只能和其他人捂手, 所以黑色情况是不算的 1和2握手2和…

vue如何实现异步组件

在 Vue 中实现异步组件&#xff0c;通常是为了在需要时加载组件&#xff0c;从而优化应用的性能&#xff0c;特别是当组件很大或者只在某些特定条件下才需要时。Vue 提供了几种方式来定义和使用异步组件。 1. 使用 import() 语法 你可以使用 ES6 的 import() 语法来动态地导入…

【测试思考】高覆盖的测试用例不只要方法

昨天临睡前看到一篇博客文章&#xff0c;看起来是一位java后端写的【转测试/测试开发】的系列文章 我把他的一系列10多篇文章都看完了&#xff0c;个人觉得特别适合刚开始接触测试或者想对测试做一些了解的朋友。 其中&#xff0c;对于测试用例的设计&#xff0c;我之前有分享一…

leetcode_43.字符串相乘

43. 字符串相乘 题目描述&#xff1a;给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 &q…

docker搭建redis集群三主三从

为什么需要做分布式redis 水平扩展&#xff1a; 随着业务的发展&#xff0c;单机Redis可能无法满足日益增长的数据存储和访问需求。分布式Redis可以通过将数据分散到多个节点上来实现水平扩展&#xff0c;提高存储容量和处理能力。高可用性&#xff1a; 单点故障是任何系统的一…

2024五一杯数学建模C题思路分享 - 煤矿深部开采冲击地压危险预测

文章目录 1 赛题选题分析 2 解题思路2.1 问题重述2.2 第一问完整思路2.2 二、三问思路更新 3 最新思路更新 1 赛题 C题 煤矿深部开采冲击地压危险预测 煤炭是中国的主要能源和重要的工业原料。然而&#xff0c;随着开采深度的增加&#xff0c;地应力增大&#xff0c;井下煤岩动…

MATLAB中功率谱密度计算pwelch函数使用详解

MATLAB中功率谱密度计算pwelch函数使用详解 目录 前言 一、pwelch函数简介 二、pwelch函数参数说明 三、pxx pwelch(x)示例 四、[pxx,f]pwelch(x,window,noverlap,nfft,fs)示例 四、[pxx,f] pwelch(x,window,noverlap,nfft,fs,freqrange,spectrumtype)示例 五、多通道功…

pinia学习1:pinia的介绍与使用方法

Pinia是一个专门为Vue.js设计的状态管理库&#xff0c;它的主要目标是提供一种更加简单、直观且可扩展的方式来管理和访问应用程序的状态。与Vuex相比&#xff0c;Pinia更加轻量级&#xff0c;并且与Vue 3的Composition API完美结合&#xff0c;使得状态管理变得更加灵活和高效…