Go设计模式(18)-观察者模式

终于写完了创建型和结构型设计模式(共12个),现在开始写行为型设计模式(共11个)。观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者一些产品的设计思路,都有这种模式的影子。

UML类图位置:https://www.processon.com/view/link/60d29bf3e401fd49502afd25

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/18observer.go

1.定义

1.1观察者模式

观察者模式:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

UML:

图片

1.2分析

观察者模式算是比较通用的设计模式了,思路也比较清晰。Subject里有Observer集合,当Subject有更新时,通知各个Observer。Subject为了管理Observer,自身设置了增加、删除功能。

当Subject有更新,通知Observer时,有很多细节值得讨论。

第一个问题是使用同步阻塞还是异步非阻塞

  • 同步阻塞是最经典的实现方式,主要是为了代码解耦;

  • 异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;

第二个问题是如何保证所有Observer都通知成功。

  • 方案一是利用消息队列ACK的能力,Observer订阅消息队列。Subject只需要确保信息通知给消息队列即可。

  • 方案二是Subject将失败的通知记录,方便后面进行重试。

  • 方案三是定义好规范,例如只对网络失败这种错误进行记录,业务失败类型不管理,由业务自行保证成功。

第三个问题是不同进程/系统如何进行通知。

  • 进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。

2.使用场景

观察者模式的使用场景还是很多的,它和享元模式一样,更多体现的是一种设计理念。

记得前些日子遇到一个场景,正好使用的是观察者模式。商家购买服务的订单,有多个状态,如交易成功、交易取消、开始履约等。业务场景只关注交易成功、交易取消和开始履约。如果不使用观察者模式,可以用if-else解决这个问题。但各个状态的处理逻辑比较复杂,而且状态有可能存在乱序的情况,导致要处理的case更多了。这时候用观察者模式就很合适,订单状态变更为Subject,具体处理逻辑为Observer,状态变更时,通知所有处理逻辑,谁合适处理便由谁处理。不但隔离性变好,扩展性也增强了。

3.代码实现

package main

import "fmt"

type PurchaseOperFunc func(status string, data string) (res bool, err error)

/**
 * @Author: Jason Pang
 * @Description: 注册的观察者
 */
var PurchaseOperFuncArr = []PurchaseOperFunc{
    
    
   create,
   isDeleted,
   apply,
}

/**
 * @Author: Jason Pang
 * @Description: 用于创建的观察者
 * @param status
 * @param data
 * @return res
 * @return err
 */
func create(status string, data string) (res bool, err error) {
    
    
   if status == "create" {
    
    
      fmt.Println("开始创建")
      return true, nil
   }
   return true, nil
}

/**
 * @Author: Jason Pang
 * @Description: 用于删除的观察者
 * @param status
 * @param data
 * @return res
 * @return err
 */
func isDeleted(status string, data string) (res bool, err error) {
    
    
   if status == "delete" {
    
    
      fmt.Println("开始删除")
      return true, nil
   }
   return true, nil
}

/**
 * @Author: Jason Pang
 * @Description: 用于履约的观察者
 * @param status
 * @param data
 * @return res
 * @return err
 */
func apply(status string, data string) (res bool, err error) {
    
    
   if status == "apply" {
    
    
      fmt.Println("开始履约")
      return true, nil
   }
   return true, nil
}

func main() {
    
    
   status := "create"
   data := "订单数据"
   //有状态更新时,通知所有观察者
   for _, oper := range PurchaseOperFuncArr {
    
    
      res, err := oper(status, data)
      if err != nil {
    
    
         fmt.Println("操作失败")
         break
      }
      if res == false {
    
    
         fmt.Println("处理失败")
         break
      }
   }
}

输出:

➜ myproject go run main.go

开始创建

这个代码是个简单版,但整体逻辑是一样的。这种写法的好处有如下几个:

  1. 如果要处理新的状态,则实现后再PurchaseOperFuncArr添加即可,改动不大

  2. 根据具体业务来说,只能有一个逻辑是真正负责执行的,所以有一个错误就报错,然后重试即可。不过具体逻辑需要做好幂等

总结

实际上,设计模式要干的事情就是解耦。创建型模式是将创建和使用代码解耦,结构型模式是将不同功能代码解耦,行为型模式是将不同的行为代码解耦,具体到观察者模式,它是将观察者和被观察者代码解耦。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

图片

往期文章回顾:

招聘

  1. 字节跳动|内推大放送

  2. 字节跳动|今日头条广州服务端研发工程师内推

  3. 字节跳动|抖音电商急招上海前端开发工程

  4. 字节跳动|抖音电商上海资深服务端开发工程师-交易

  5. 字节跳动|抖音电商武汉服务端(高级)开发工程师

  6. 字节跳动|飞书大客户产品经理内推咯

  7. 字节跳动|抖音电商服务端技术岗位虚位以待

  8. 字节跳动招聘专题

设计模式

  1. Go设计模式(17)-享元模式

  2. Go设计模式(16)-组合模式

  3. Go设计模式(15)-门面模式

  4. Go设计模式(14)-适配器模式

  5. Go设计模式(13)-装饰器模式

  6. Go设计模式(12)-桥接模式

  7. Go设计模式(11)-代理模式

  8. Go设计模式(10)-原型模式

  9. Go设计模式(9)-建造者模式

  10. Go设计模式(8)-抽象工厂

  11. Go设计模式(7)-工厂模式

  12. Go设计模式(6)-单例模式

  13. Go设计模式(5)-类图符号表示法

  14. Go设计模式(4)-代码编写优化

  15. Go设计模式(4)-代码编写

  16. Go设计模式(3)-设计原则

  17. Go设计模式(2)-面向对象分析与设计

  18. Go设计模式(1)-语法

语言

  1. 再也不怕获取不到Gin请求数据了

  2. 一文搞懂pprof

  3. Go工具之generate

  4. Go单例实现方案

  5. Go通道实现原理

  6. Go定时器实现原理

  7. Beego框架使用

  8. Golang源码BUG追查

  9. Gin框架简洁版

  10. Gin源码剖析

架构

  1. 分页复选设计的坑

  2. 支付接入常规问题

  3. 限流实现2

  4. 秒杀系统

  5. 分布式系统与一致性协议

  6. 微服务之服务框架和注册中心

  7. 浅谈微服务

  8. 限流实现1

  9. CDN请求过程详解

  10. 常用缓存技巧

  11. 如何高效对接第三方支付

  12. 算法总结

存储

  1. 一文搞懂MySQL数据库分库分表

  2. MySQL开发规范

  3. Redis实现分布式锁

  4. 事务原子性、一致性、持久性的实现原理

  5. InnoDB锁与事务简析

网络

  1. HTTP2.0基础教程

  2. HTTPS配置实战

  3. HTTPS连接过程

  4. TCP性能优化

工具

  1. GoLand实用技巧

  2. 根据mysql表自动生成go struct

  3. Markdown编辑器推荐-typora

读书笔记

  1. 《毛选》推荐

  2. 原则

  3. 资治通鉴

  4. 敏捷革命

  5. 如何锻炼自己的记忆力

  6. 简单的逻辑学-读后感

  7. 热风-读后感

  8. 论语-读后感

  9. 孙子兵法-读后感

思考

  1. 对过去的一点回顾

  2. 对写博客的一些思考

  3. 晚上打119的经历

  4. 为动员一切力量争取胜利而斗争

  5. 反对自由主义

  6. 实践论

  7. 评价自己的标准

  8. 2020博客总结

  9. 服务端团队假期值班方案

  10. 项目流程管理

  11. 对项目管理的一些看法

  12. 对产品经理的一些思考

  13. 关于程序员职业发展的思考

  14. 关于代码review的思考

猜你喜欢

转载自blog.csdn.net/shida219/article/details/118270071