Golang 设计模式(行为型)

文章目录

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

策略模式

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

  • 场景

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

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

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

package main

import "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)
}
  • 输出结果
使用信用卡支付:100.50 元
使用现金支付:50.25 元

迭代器模式

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

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

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

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

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

package main

import "fmt"

// Iterator 迭代器接口
type Iterator interface {
	HasNext() bool
	Next() interface{}
}

// Aggregate 聚合接口
type Aggregate interface {
	CreateIterator() Iterator
}

// ConcreteIterator 具体迭代器
type ConcreteIterator struct {
	index int
	data  []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)
	}
}
  • 输出结果
当前元素: 元素A
当前元素: 元素B
当前元素: 元素C

访问者模式

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

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

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

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

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

package main

import "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)
}
  • 输出结果
访问者访问具体元素A
访问者访问具体元素B

观察者模式

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

  • 场景

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

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

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

package main

import "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()

	// 移除观察者A
	subject.RemoveObserver(observerA)

	// 再次发送通知给观察者
	subject.NotifyObservers()
}
  • 输出结果
观察者 观察者A 收到通知:有新消息
观察者 观察者B 收到通知:有新消息
观察者 观察者B 收到通知:有新消息

命令模式

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

  • 场景

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

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

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

package main

import "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()
}
  • 输出结果
接收者执行操作1
接收者执行操作2

模板方法模式

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

  • 场景

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

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

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

package main

import "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()
}
  • 输出结果
调用模板方法
具体方法1的实现
具体方法2的实现

责任链模式

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

  • 场景

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

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

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

package main

import "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)
}
  • 输出结果
处理器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 main

import "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() {
	// 创建上下文,并设置初始状态为状态A
	context := &Context{state: &ConcreteStateA{}}

	// 不断发出请求,观察状态的变化
	context.Request()
	context.Request()
	context.Request()
	context.Request()
}
  • 输出结果
处理状态A
处理状态B
处理状态A
处理状态B

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/584627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python俄罗斯方块

文章目录 游戏实现思路1. 游戏元素的定义2. 游戏区域和状态的定义3. 游戏逻辑的实现4. 游戏界面的绘制5. 游戏事件的处理6. 游戏循环7. 完整实现代码 游戏实现思路 这个游戏的实现思路主要分为以下几个步骤&#xff1a; 1. 游戏元素的定义 Brick类&#xff1a;表示游戏中的砖…

CSS样式特异性5层次详解

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

云端芳华、运维之美

今天&#xff0c;在我们享受互联网服务带来的便利与高效的同时&#xff0c;有一群人默默地在幕后为我们提供支持&#xff0c;他们就是云端运维人员。 值此五一国际劳动节来临之际&#xff0c;我们要真诚感谢他们辛勤的劳动和奉献&#xff01;

基于ssm+vue开放式教学评价管理系统【ppt·代码·文档报告】

项目演示视频 项目名称&#xff1a;开放式教学评价管理系统 系统介绍&#xff1a;本系统是通过java的SSM框架来实现的&#xff0c;前端采用vue框架进行实现 管理员通过登录进入到系统操作界面&#xff0c;结合需求可以对个人信息进行在线修改维护&#xff0c;也可结合需求进行…

经典文献阅读之--SurroundOcc(自动驾驶的环视三维占据栅格预测)

0. 简介 环视BEV已经是很多场景中需要的功能&#xff0c;也是视觉代替激光雷达的有效解决方案&#xff0c;而《SurroundOcc: Multi-camera 3D Occupancy Prediction for Autonomous Driving》一吻则代表了这个领域的SOTA算法&#xff0c;文中通过多帧点云构建了稠密占据栅格数据…

【设计模式】抽象工厂模式(Abstract Factory Pattern)

目录标题 抽象工厂设计模式详解1. 介绍2. 结构3. 实现步骤3.1 创建抽象产品接口3.2 创建具体产品类3.3 创建抽象工厂接口3.4 创建具体工厂类 4. 好处与优点5. 坏处与缺点6. 适用场景7. 总结 抽象工厂设计模式详解 1. 介绍 抽象工厂模式是一种创建型设计模式&#xff0c;它提供…

Kimichat使用技巧:方便又实用的kimi+智能体

今天kimi智能助手推出了kimi的功能。简单的说&#xff0c;就是一系列kimi已经写好的提示词&#xff0c;用户可以直接调用、对话。 Kimi分为官方推荐、办公提效、辅助写作、社交娱乐、生活实用这几类。可以从左边侧边栏点击进入。 官方推荐的有&#xff1a; Kimi 001号小客服&…

c语言——二叉树

目录 目录 二叉树关键概念理解 一颗拥有1000个结点的树度为4&#xff0c;则它的最小深度是&#xff1f; 那么对于二叉树&#xff0c;只掌握这些是远远不够的&#xff0c;我们还需要掌握几个最基本的经典问题&#xff0c; 求二叉树大小 求叶子结点个数 求深度 求第k层的…

多线程编程9——线程池

一、为什么要引入线程池&#xff1f; 虽然对比进程&#xff0c;线程已经很轻量了&#xff0c;创建销毁调度线程都更高效。但是随着并发程度的提高&#xff0c;我们对于性能要求的标准也越来越高&#xff0c;当我们需要频繁创建销毁调度线程时&#xff0c;就发现线程也没有那么…

【基础算法】二分查找

1.二分查找 二分查找 思路&#xff1a; 朴素二分模版 class Solution { public:int search(vector<int>& nums, int target) {int l 0, r nums.size() - 1;while(l < r){int mid (l r) / 2;if(nums[mid] < target) l mid 1;else if(nums[mid] > ta…

综合性练习(后端代码练习1)——加法计算器

目录 一、准备工作 二、约定前后端交互接口 1、概念介绍 2、需求分析 3、接口定义 请求参数 响应数据 三、服务器代码 四、前端页面代码 五、运行测试 遇到问题如何解决&#xff1f; 需求&#xff1a;输入两个整数&#xff0c;点击 “点击相加” 按钮&#xff0c;显…

JAVA顺序表相关习题1

1.笔试题:cvte str1 :welcome to cvte str2:come 描述:删除第一个字符串当中出现的所有的第二个字符串的字符!结果:wlt vt 要求 用ArrayList完成! public class Test {public static List<Character> findSameWords(String u1, String u2){List<Character> listn…

前端请求没问题,后端正常运行,但查不出数据

写代码时写得快了些&#xff0c;Orders.的订单状态写错了CONFIRMED 改成COMPLETED

二、再识VUE-MVVM

一、初识VUE 二、再识VUE-MVVM 三、VUE数据代理 MVVM Vue.js 专注于 MVVM 模型的 ViewModel 层。它通过双向数据绑定把 View 层和 Model 层连接了起来。实际的 DOM 封装和输出格式都被抽象为了 Directives 和 Filters。 ViewModel 一个同步 Model 和 View 的对象。在 Vue.js…

汇川AM400PLC编码器转速测量功能块(M法测速)

M法测速的原理和相关代码,大家可以参考相关专栏文章,常用链接如下: 1、编码器M法测速仿真 编码器M法测速仿真(Simulink)_mt法测速 simulink-CSDN博客文章浏览阅读2k次。编码器M法和T法测速的详细讲解可以参看下面的文章链接,这里不再赘述,这里主要介绍Simulink里建模仿真…

(06)vite与ts的结合

文章目录 系列全集package.json在根目录创建 tsconfig.json 文件在根目录创建 vite.config.ts 文件index.html额外的类型声明 系列全集 &#xff08;01&#xff09;vite 从启动服务器开始 &#xff08;02&#xff09;vite环境变量配置 &#xff08;03&#xff09;vite 处理 c…

详细介绍如何使用YOLOv9 在医疗数据集上进行实例分割-含源码+数据集下载

深度学习彻底改变了医学图像分析。通过识别医学图像中的复杂模式,它可以帮助我们解释有关生物系统的重要见解。因此,如果您希望利用深度学习进行医疗诊断,本文可以成为在医疗数据集上微调YOLOv9 实例分割的良好起点。 实例分割模型不是简单地将区域分类为属于特定细胞类型,…

新质生产力实践,我用chatgpt开发网站

是的&#xff0c;我用chatgpt开发了一个网站&#xff0c;很轻松。 我之前一点不懂前端&#xff0c;也没有网站开发的代码基础&#xff0c;纯正的0基础。 从0开始到最后成品上线&#xff0c;时间总计起来大致一共花了2-3周的时间。 初始想法我是想给我公司开发一个网站&#…

3月8日是星期六

突然有查询特殊条件日期的需求。 <html> <title>3月8日是星期六</title> <center> <h1 id"h1"></h1> <div id"div"></div> </center> <script> var weekday [星期日, 星期一, 星期二, 星期…

Eclipse:-Dmaven.multiModuleProjectDirectory system propery is not set.

eclipse中使用maven插件的时候&#xff0c;运行run as maven build的时候报错 -Dmaven.multiModuleProjectDirectory system propery is not set. Check $M2_HOME environment variable and mvn script match. 可以设一个环境变量M2_HOME指向你的maven安装目录 M2_HOMED:\Apps\…
最新文章