Golang 设计模式(行为型)

文章目录

策略模式

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

  • 场景

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

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

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

go 复制代码
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)
}
  • 输出结果
bash 复制代码
使用信用卡支付:100.50 元
使用现金支付:50.25 元

迭代器模式

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

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

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

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

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

go 复制代码
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)
	}
}
  • 输出结果
bash 复制代码
当前元素: 元素A
当前元素: 元素B
当前元素: 元素C

访问者模式

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

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

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

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

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

go 复制代码
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)
}
  • 输出结果
bash 复制代码
访问者访问具体元素A
访问者访问具体元素B

观察者模式

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

  • 场景

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

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

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

go 复制代码
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()
}
  • 输出结果
bash 复制代码
观察者 观察者A 收到通知:有新消息
观察者 观察者B 收到通知:有新消息
观察者 观察者B 收到通知:有新消息

命令模式

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

  • 场景

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

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

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

go 复制代码
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()
}
  • 输出结果
bash 复制代码
接收者执行操作1
接收者执行操作2

模板方法模式

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

  • 场景

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

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

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

go 复制代码
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()
}
  • 输出结果
bash 复制代码
调用模板方法
具体方法1的实现
具体方法2的实现

责任链模式

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

  • 场景

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

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

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

go 复制代码
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)
}
  • 输出结果
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. 状态切换的开销: 状态模式将状态和状态相关的行为封装在不同的状态对象中,当状态切换时可能会导致对象的状态转换开销增加,特别是当有大量的状态和状态转换时。
  • 示例

go 复制代码
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()
}
  • 输出结果
bash 复制代码
处理状态A
处理状态B
处理状态A
处理状态B
相关推荐
aaasssdddd962 小时前
C++的封装(十四):《设计模式》这本书
数据结构·c++·设计模式
T1an-12 小时前
设计模式之【观察者模式】
观察者模式·设计模式
hkNaruto3 小时前
【P2P】【Go】采用go语言实现udp hole punching 打洞 传输速度测试 ping测试
golang·udp·p2p
入 梦皆星河3 小时前
go中常用的处理json的库
golang
思忖小下4 小时前
梳理你的思路(从OOP到架构设计)_设计模式Factory Method模式
设计模式·工厂方法模式·eit
霁月风5 小时前
设计模式——工厂方法模式
c++·设计模式·工厂方法模式
海绵波波1075 小时前
Gin-vue-admin(2):项目初始化
vue.js·golang·gin
每天写点bug5 小时前
【go每日一题】:并发任务调度器
开发语言·后端·golang
一个不秃头的 程序员5 小时前
代码加入SFTP Go ---(小白篇5)
开发语言·后端·golang
基哥的奋斗历程6 小时前
初识Go语言
开发语言·后端·golang