【设计模式】观察者,只旁观?不,还可随之变化

观察者是行为设计模式

GoF定义:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新

场景

积分系统:消费积分记录、赚取积分记录,需要根据订单状态变化进行不同逻辑的处理。

消费积分记录

未付款->支付,进行消费积分核销

赚取积分记录

未付款->支付,进行赚取积分的冻结

发货->完成,进行赚取积分的解冻

怎么样?是不是觉得观察者模式非常适用

没错,观察者非常适用一对多依赖关系的场景,例如事件驱动系统、状态变化通知等。

实现

订单状态

go 复制代码
const (  
    pendingOrderStatus    = "pending"    // 未支付状态  
    processingOrderStatus = "processing" // 支付状态  
    shipmentOrderStatus   = "shipment"   // 发货状态  
    completeOrderStatus   = "complete"   // 完成状态  
    cancelOrderStatus     = "cancel"     // 取消状态  
)

接口

规范观察需要实现,通过这个可以收到观察对象的变化

go 复制代码
type OrderStateObserver interface {  
    Update(subject *Subject)  
}

订单

示例中需要观察的对象,主要是观察订单状态的变化

go 复制代码
type Subject struct {  
    observers []OrderStateObserver  // 观察者列表
    stateOld  string  // 订单历史状态
    stateNow  string  // 订单当前状态
}  

// Attach 添加监听者
func (s *Subject) Attach(observer OrderStateObserver) {  
    s.observers = append(s.observers, observer)  
}  

// Detach 删除监听者
func (s *Subject) Detach(observer OrderStateObserver) {  
    for i, item := range s.observers {  
       if observer == item {  
          s.observers = append(s.observers[:i], s.observers[i+1:]...)  
       }  
    }  
}  
  
func (s *Subject) notify() {  
    for _, observer := range s.observers {  
       observer.Update(s)  
    }  
}  
  
func (s *Subject) GetStateNow() string {  
    return s.stateNow  
}  
func (s *Subject) GetStateOld() string {  
    return s.stateOld  
}  
  
func (s *Subject) SetSate(state string) {  
    s.stateOld = s.stateNow  
    s.stateNow = state  
    s.notify()  
}


type OrderSubject struct {  
    Subject  
}  
  
func NewOrderSubject(state string) *OrderSubject {  
    return &OrderSubject{  
       Subject{stateNow: state},  
    }  
}

Attach 添加观察者
Detach 删除观察者

订单的状态变化可以通过 notify 通知到观察者

积分使用记录

观察者,随订单状态变化,做出对应的逻辑

go 复制代码
// ScoreUseRecordObserver 积分使用记录  
type ScoreUseRecordObserver struct {  
    objectState string  
}  
  
func (c *ScoreUseRecordObserver) Update(subject *Subject) {  
    c.objectState = subject.GetStateNow()  
    // 此处可以进行积分使用记录的相关操作,略过,只是输出一行日志  
    fmt.Printf("积分使用记录, 执行观察者操作! 状态: %v -> %v\n", subject.GetStateOld(), c.objectState)  
}  
  
func NewScoreUseRecordObserver() *ScoreUseRecordObserver {  
    return &ScoreUseRecordObserver{}  
}

积分获取记录

观察者,随订单状态变化,做出对应的逻辑

go 复制代码
// ScoreGetRecordObserver 积分获取记录  
type ScoreGetRecordObserver struct {  
    objectState string  
}  
  
func (c *ScoreGetRecordObserver) Update(subject *Subject) {  
    c.objectState = subject.GetStateNow()  
    // 此处可以进行积分获取记录的相关操作,略过,只是输出一行日志  
    fmt.Printf("积分获取记录, 执行观察者操作! 状态: %v -> %v\n", subject.GetStateOld(), c.objectState)  
}  
  
func NewScoreGetRecordObserver() *ScoreGetRecordObserver {  
    return &ScoreGetRecordObserver{}  
}

订单状态变化

当状态发生变化时(如从 pending 变为 processing),观察者会收到通知并执行相应的逻辑。

go 复制代码
orderSubject := NewOrderSubject(pendingOrderStatus)  

// 积分使用记录  
scoreUseRecordObserver := NewScoreUseRecordObserver()  
orderSubject.Attach(scoreUseRecordObserver)  

// 积分获取记录  
scoreGetRecordObserver := NewScoreGetRecordObserver()  
orderSubject.Attach(scoreGetRecordObserver)  

// 状态变成支付状态  
orderSubject.SetSate(processingOrderStatus)  
// 状态变成发货状态  
orderSubject.SetSate(shipmentOrderStatus)  

// 取消积分使用记录的监听  
orderSubject.Detach(scoreUseRecordObserver)  

// 状态变成完成状态  
orderSubject.SetSate(completeOrderStatus)  

完整代码

go 复制代码
package main  
  
// 观察者模式  
  
import (  
    "fmt"  
    "testing")  
  
const (  
    pendingOrderStatus    = "pending"    // 未支付状态  
    processingOrderStatus = "processing" // 支付状态  
    shipmentOrderStatus   = "shipment"   // 发货状态  
    completeOrderStatus   = "complete"   // 完成状态  
    cancelOrderStatus     = "cancel"     // 取消状态  
)  
  
type OrderStateObserver interface {  
    Update(subject *Subject)  
}  
  
// ScoreUseRecordObserver 积分使用记录  
type ScoreUseRecordObserver struct {  
    objectState string  
}  
  
func (c *ScoreUseRecordObserver) Update(subject *Subject) {  
    c.objectState = subject.GetStateNow()  
    // 此处可以进行积分使用记录的相关操作,略过,只是输出一行日志  
    fmt.Printf("积分使用记录, 执行观察者操作! 状态: %v -> %v\n", subject.GetStateOld(), c.objectState)  
}  
  
func NewScoreUseRecordObserver() *ScoreUseRecordObserver {  
    return &ScoreUseRecordObserver{}  
}  
  
// ScoreGetRecordObserver 积分获取记录  
type ScoreGetRecordObserver struct {  
    objectState string  
}  
  
func (c *ScoreGetRecordObserver) Update(subject *Subject) {  
    c.objectState = subject.GetStateNow()  
    // 此处可以进行积分获取记录的相关操作,略过,只是输出一行日志  
    fmt.Printf("积分获取记录, 执行观察者操作! 状态: %v -> %v\n", subject.GetStateOld(), c.objectState)  
}  
  
func NewScoreGetRecordObserver() *ScoreGetRecordObserver {  
    return &ScoreGetRecordObserver{}  
}  
  
type Subject struct {  
    observers []OrderStateObserver  
    stateOld  string  
    stateNow  string  
}  

// Attach 添加监听者
func (s *Subject) Attach(observer OrderStateObserver) {  
    s.observers = append(s.observers, observer)  
}  

// Attach 删除监听者
func (s *Subject) Detach(observer OrderStateObserver) {  
    for i, item := range s.observers {  
       if observer == item {  
          s.observers = append(s.observers[:i], s.observers[i+1:]...)  
       }  
    }  
}  
  
func (s *Subject) notify() {  
    for _, observer := range s.observers {  
       observer.Update(s)  
    }  
}  
  
func (s *Subject) GetStateNow() string {  
    return s.stateNow  
}  
func (s *Subject) GetStateOld() string {  
    return s.stateOld  
}  
  
func (s *Subject) SetSate(state string) {  
    s.stateOld = s.stateNow  
    s.stateNow = state  
    s.notify()  
}  
  
type OrderSubject struct {  
    Subject  
}  
  
func NewOrderSubject(state string) *OrderSubject {  
    return &OrderSubject{  
       Subject{stateNow: state},  
    }  
}  
  
func main() {  
    // 消费订单状态变化消息  
    // 这里略过  
  
    orderSubject := NewOrderSubject(pendingOrderStatus)  
  
    // 积分使用记录  
    scoreUseRecordObserver := NewScoreUseRecordObserver()  
    orderSubject.Attach(scoreUseRecordObserver)  
  
    // 积分获取记录  
    scoreGetRecordObserver := NewScoreGetRecordObserver()  
    orderSubject.Attach(scoreGetRecordObserver)  
  
    // 状态变成支付状态  
    orderSubject.SetSate(processingOrderStatus)  
    // 状态变成发货状态  
    orderSubject.SetSate(shipmentOrderStatus)  
  
    // 取消积分使用记录的监听  
    orderSubject.Detach(scoreUseRecordObserver)  
  
    // 状态变成完成状态  
    orderSubject.SetSate(completeOrderStatus)  
}

总结

怎样?是不是觉得代码很优雅?

没错,观察者体现了对扩展开放、修改关闭设计原则。

1.订单状态和观察者之间通过接口交互,降低了依赖。

2.可以轻松添加新的观察者,而无需修改订单相关代码。

3.观察者可以动态地添加或移除。

观察者模式,可以比做科幻作品的穿越者,穿越者在历史事件发生的时候,在旁边记录着各种各样的人或事。

但设计模式中,观察者还可以随主体状态改变来做出对应的动作

相关推荐
UVM_ERROR27 分钟前
最近在工作中感受到了设计模式的重要性
java·开发语言·设计模式
听闻风很好吃29 分钟前
Java设计模式之中介者模式:从入门到架构级实践
java·设计模式·中介者模式
风象南42 分钟前
Redis管道技术:提升Java应用中的Redis操作性能
redis·后端
小李小李快乐不已43 分钟前
3.3.2 应用层协议设计protobuf(二进制序列化协议)
linux·c++·后端·网络协议·信息与通信
程序员JerrySUN43 分钟前
设计模式每日硬核训练 Day 11:适配器模式(Adapter Pattern)完整讲解与实战应用
java·设计模式·适配器模式
未定义.2217 小时前
Java设计模式实战:策略模式在SimUDuck问题中的应用
java·设计模式·策略模式
东阳马生架构9 小时前
Sentinel源码—1.使用演示和简介
后端
zhuyasen10 小时前
首个与AI深度融合的Go开发框架sponge,解决Cursor/Trae等工具项目级开发痛点
后端·低代码·go
山有木兮丶丶10 小时前
spring boot大文件与多文件下载
spring boot·后端
余瑾瑜10 小时前
如何在CentOS部署青龙面板并实现无公网IP远程访问本地面板
开发语言·后端·golang