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

观察者是行为设计模式

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.观察者可以动态地添加或移除。

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

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

相关推荐
笃行3503 小时前
SQLServer数据迁移之后,那张报表还能不能秒出
后端
LucianaiB4 小时前
别再被 AI 骗了:我把腾讯云 4 个 Skill 做成了个「AI 打假侦探」,过程的一个小配置坑惨了我
后端
candyTong4 小时前
Claude Code 如何恢复一段会话
后端·架构·ai编程
En^_^Joy5 小时前
Django项目配置全攻略:settings配置文件
后端·python·django
IT_陈寒8 小时前
Python线程池吞了异常还不告诉我,这谁顶得住啊
前端·人工智能·后端
小强库计算机毕业设计8 小时前
SpringBoot+Vue3 学生宿舍管理系统实战
java·spring boot·后端·vue·学生宿舍管理系统
小狼1838 小时前
实践6|SDD 实战:AI 写的规格被推翻了四次
后端·claude
篮框坏了8 小时前
"DeepSeek Harness 实测:一毛钱干三活,但默认配置是个坑"
后端·ai编程
jobBridge218 小时前
大模型到底是怎么"想"的?我把 Transformer 拆开,发现它其实是个"接词狂魔"
人工智能·后端·编程语言
唐青枫9 小时前
看懂内存地址之后,才算真正入门 Zig:指针、切片与实战
后端