Связанные вызовы | Шаблоны Go Design в действии

Go Шаблоны проектирования

Что ж, серия боевых шаблонов дизайна Go, серия golang, фактически используемая бизнесом шаблонов проектирования.

Полная серия нажмите здесь

предисловие

В этой серии в основном рассказывается, как использовать шаблоны проектирования в наших реальных бизнес-сценариях.

Эта серия статей в основном имеет следующую структуру:

  • Что такое «Шаблон проектирования XX»?
  • В каких реальных бизнес-сценариях можно использовать «шаблон проектирования XX»?
  • Как использовать «Шаблон дизайна XX»?

В этой статье в основном рассказывается, как «Модель цепочки ответственности» используется в реальных бизнес-сценариях.

Что такое «Модель цепочки ответственности»?

Сначала разбейте ряд предприятий на разные объекты по их обязанностям, затем сформируйте цепочку из этого ряда объектов, а затем передайте объект запроса в этот ряд объектов до их обработки.

Из концепции видно, что паттерн «Цепочка ответственности» имеет следующие очевидные преимущества:

  • По сфере ответственности: Развязка
  • Цепочки объектов: четкая логика

Но одно直到被处理为止, что означает, что фактическая бизнес-логика будет выполняться только реальным бизнес-объектом в конце, и существует не так много сценариев, которые явно применимы. Но, кроме того, два вышеупомянутых преимущества по-прежнему очень интересны, поэтому, чтобы применить их к подавляющему большинству бизнес-сценариев, которые в настоящее время находятся в контакте, концепция была просто скорректирована следующим образом:

Сначала ряд предприятий разбивается на разные объекты в соответствии с их обязанностями, а затем ряды объектов формируются в цепочку до тех пор, пока «звено не закончится». (Конец: Аномальный конец или конец выполнения ссылки)

просто直到“链路结束”为止Трансформация позволяет нам применять шаблон цепочки ответственности к любому сложному бизнес-сценарию.

Ниже приведены конкретные преимущества модели цепочки ответственности:

  • Интуитивно понятный: быстрый процесс вызова бизнеса
  • Бесконечно масштабируемая: бесконечно масштабируемая бизнес-логика
  • Высоко инкапсулированный: сложный бизнес-код по-прежнему высоко инкапсулирован.
  • Его легко модифицировать: для изменения кода в рамках сложного бизнес-кода необходимо сосредоточиться только на соответствующем файле бизнес-класса (структуре) и порядке выполнения бизнес-процессов, который можно легко настроить.

В каких реальных бизнес-сценариях можно использовать «Модель цепочки ответственности (модифицированная)»?

Сценарии, отвечающие следующим требованиям:

Все сценарии с чрезвычайно сложным бизнесом

Любой неорганизованный бизнес-код можно реорганизовать и спроектировать с использованием шаблона цепочки ответственности (модифицированного).

В каких реальных бизнес-сценариях мы можем использовать «Модель цепочки ответственности (изменение)»?

Например, интерфейс заказа системы электронной коммерции, с постоянным развитием бизнеса, интерфейс будет полон различной бизнес-логики.

Как использовать «Модель цепочки ответственности (изменение)»?

Что касается того, как его использовать, вполне возможно скопировать четыре шага, которые я суммировал для использования шаблонов проектирования:

  • Деловой кардинг
  • Блок-схема бизнеса
  • моделирование кода
  • демонстрация кода

Деловой кардинг

шаг логика
1 проверка параметров
2 Получить адресную информацию
3 проверка адресной информации
4 Получить данные корзины
5 Получить информацию о товарных запасах
6 Проверка товарных запасов
7 Получить предложения
8 Получить информацию о доставке
9 Используйте информацию о скидках
10 вычет инвентаря
11 очистить корзину
12 написать форму заказа
13 написать форму заказа
14 Напишите форму информации о скидке заказа
XX И логика, которая будет добавлена ​​в будущем...

Бизнес постоянно развивается:

  • Добавлен новый бизнес
  • старый бизнес изменен

Например, в новом бизнесе добавлена ​​предварительная продажа депозита:

  • существует4|获取购物车数据После этого необходимо проверить логику правомерности предпродажной активности депозита.
  • логика ожидания

Примечание. Процесс может быть не совсем точным.

Блок-схема бизнеса

Мы получили следующую блок-схему бизнес-процесса, объединив текст бизнес-процесса:

моделирование кода

Основные классы модели цепочки ответственности в основном включают следующие функции:

  • Свойства члена
    • nextHandler: следующий экземпляр объекта, ожидающий вызова -> стабильный
  • метод члена
    • SetNext: привязать следующий экземпляр объекта к текущему объекту.nextHandlerатрибут -> стабильный
    • Do: Запись бизнес-логики текущего объекта -> изменена
    • Run: вызов текущего объектаDo,nextHandlerЗвоните, если не пустоnextHandler.Do-> стабильный

Псевдокод, примененный к интерфейсу ордера, реализован следующим образом:

一个父类(抽象类):

- 成员属性
	+ `nextHandler`: 下一个等待被调用的对象实例
- 成员方法
	+ 实体方法`SetNext`: 实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上
	+ 抽象方法`Do`: 当前对象业务逻辑入口
	+ 实体方法`Run`: 实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`

子类一(参数校验)
- 继承抽象类父类
- 实现抽象方法`Do`:具体的参数校验逻辑

子类二(获取地址信息)
- 继承抽象类父类
- 实现抽象方法`Do`:具体获取地址信息的逻辑

子类三(获取购物车数据)
- 继承抽象类父类
- 实现抽象方法`Do`:具体获取购物车数据的逻辑

......略

子类X(以及未来会增加的逻辑)
- 继承抽象类父类
- 实现抽象方法`Do`:以及未来会增加的逻辑

Однако концепция наследования, которой нет в golang, требует повторного использования атрибутов членов.nextHandler, метод членаSetNext, метод членаRunКак это сделать? Мы используем合成复用Характеристики замаскированного для достижения цели «наследования и повторного использования» следующие:

一个接口(interface):

- 抽象方法`SetNext`: 待实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上
- 抽象方法`Do`: 待实现当前对象业务逻辑入口
- 抽象方法`Run`: 待实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`

一个基础结构体:

- 成员属性
	+ `nextHandler`: 下一个等待被调用的对象实例
- 成员方法
	+ 实体方法`SetNext`: 实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上
	+ 实体方法`Run`: 实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`

子类一(参数校验)
- 合成复用基础结构体
- 实现抽象方法`Do`:具体的参数校验逻辑

子类二(获取地址信息)
- 合成复用基础结构体
- 实现抽象方法`Do`:具体获取地址信息的逻辑

子类三(获取购物车数据)
- 合成复用基础结构体
- 实现抽象方法`Do`:具体获取购物车数据的逻辑

......略

子类X(以及未来会增加的逻辑)
- 合成复用基础结构体
- 实现抽象方法`Do`:以及未来会增加的逻辑

Также получили нашу диаграмму UML:

демонстрация кода

package main

//---------------
//我的代码没有`else`系列
//责任链模式
//@auhtor TIGERB<https://github.com/TIGERB>
//---------------

import (
	"fmt"
	"runtime"
)

// Context Context
type Context struct {
}

// Handler 处理
type Handler interface {
	// 自身的业务
	Do(c *Context) error
	// 设置下一个对象
	SetNext(h Handler) Handler
	// 执行
	Run(c *Context) error
}

// Next 抽象出来的 可被合成复用的结构体
type Next struct {
	// 下一个对象
	nextHandler Handler
}

// SetNext 实现好的 可被复用的SetNext方法
// 返回值是下一个对象 方便写成链式代码优雅
// 例如 nullHandler.SetNext(argumentsHandler).SetNext(signHandler).SetNext(frequentHandler)
func (n *Next) SetNext(h Handler) Handler {
	n.nextHandler = h
	return h
}

// Run 执行
func (n *Next) Run(c *Context) (err error) {
	// 由于go无继承的概念 这里无法执行当前handler的Do
	// n.Do(c)
	if n.nextHandler != nil {
		// 合成复用下的变种
		// 执行下一个handler的Do
		if err = (n.nextHandler).Do(c); err != nil {
			return
		}
		// 执行下一个handler的Run
		return (n.nextHandler).Run(c)
	}
	return
}

// NullHandler 空Handler
// 由于go无继承的概念 作为链式调用的第一个载体 设置实际的下一个对象
type NullHandler struct {
	// 合成复用Next的`nextHandler`成员属性、`SetNext`成员方法、`Run`成员方法
	Next
}

// Do 空Handler的Do
func (h *NullHandler) Do(c *Context) (err error) {
	// 空Handler 这里什么也不做 只是载体 do nothing...
	return
}

// ArgumentsHandler 校验参数的handler
type ArgumentsHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *ArgumentsHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "校验参数成功...")
	return
}

// AddressInfoHandler 地址信息handler
type AddressInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *AddressInfoHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "获取地址信息...")
	fmt.Println(runFuncName(), "地址信息校验...")
	return
}

// CartInfoHandler 获取购物车数据handler
type CartInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *CartInfoHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "获取购物车数据...")
	return
}

// StockInfoHandler 商品库存handler
type StockInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *StockInfoHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "获取商品库存信息...")
	fmt.Println(runFuncName(), "商品库存校验...")
	return
}

// PromotionInfoHandler 获取优惠信息handler
type PromotionInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *PromotionInfoHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "获取优惠信息...")
	return
}

// ShipmentInfoHandler 获取运费信息handler
type ShipmentInfoHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *ShipmentInfoHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "获取运费信息...")
	return
}

// PromotionUseHandler 使用优惠信息handler
type PromotionUseHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *PromotionUseHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "使用优惠信息...")
	return
}

// StockSubtractHandler 库存操作handler
type StockSubtractHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *StockSubtractHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "扣库存...")
	return
}

// CartDelHandler 清理购物车handler
type CartDelHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *CartDelHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "清理购物车...")
	// err = fmt.Errorf("CartDelHandler.Do fail")
	return
}

// DBTableOrderHandler 写订单表handler
type DBTableOrderHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *DBTableOrderHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "写订单表...")
	return
}

// DBTableOrderSkusHandler 写订单商品表handler
type DBTableOrderSkusHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *DBTableOrderSkusHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "写订单商品表...")
	return
}

// DBTableOrderPromotionsHandler 写订单优惠信息表handler
type DBTableOrderPromotionsHandler struct {
	// 合成复用Next
	Next
}

// Do 校验参数的逻辑
func (h *DBTableOrderPromotionsHandler) Do(c *Context) (err error) {
	fmt.Println(runFuncName(), "写订单优惠信息表...")
	return
}

// 获取正在运行的函数名
func runFuncName() string {
	pc := make([]uintptr, 1)
	runtime.Callers(2, pc)
	f := runtime.FuncForPC(pc[0])
	return f.Name()
}

func main() {
	// 初始化空handler
	nullHandler := &NullHandler{}

	// 链式调用 代码是不是很优雅
	// 很明显的链 逻辑关系一览无余
	nullHandler.SetNext(&ArgumentsHandler{}).
		SetNext(&AddressInfoHandler{}).
		SetNext(&CartInfoHandler{}).
		SetNext(&StockInfoHandler{}).
		SetNext(&PromotionInfoHandler{}).
		SetNext(&ShipmentInfoHandler{}).
		SetNext(&PromotionUseHandler{}).
		SetNext(&StockSubtractHandler{}).
		SetNext(&CartDelHandler{}).
		SetNext(&DBTableOrderHandler{}).
		SetNext(&DBTableOrderSkusHandler{}).
		SetNext(&DBTableOrderPromotionsHandler{})
		//无限扩展代码...

	// 开始执行业务
	if err := nullHandler.Run(&Context{}); err != nil {
		// 异常
		fmt.Println("Fail | Error:" + err.Error())
		return
	}
	// 成功
	fmt.Println("Success")
	return
}

Результат запуска кода:

[Running] go run "../easy-tips/go/src/patterns/responsibility/responsibility-order-submit.go"
main.(*ArgumentsHandler).Do 校验参数成功...
main.(*AddressInfoHandler).Do 获取地址信息...
main.(*AddressInfoHandler).Do 地址信息校验...
main.(*CartInfoHandler).Do 获取购物车数据...
main.(*StockInfoHandler).Do 获取商品库存信息...
main.(*StockInfoHandler).Do 商品库存校验...
main.(*PromotionInfoHandler).Do 获取优惠信息...
main.(*ShipmentInfoHandler).Do 获取运费信息...
main.(*PromotionUseHandler).Do 使用优惠信息...
main.(*StockSubtractHandler).Do 扣库存...
main.(*CartDelHandler).Do 清理购物车...
main.(*DBTableOrderHandler).Do 写订单表...
main.(*DBTableOrderSkusHandler).Do 写订单商品表...
main.(*DBTableOrderPromotionsHandler).Do 写订单优惠信息表...
Success

Эпилог

Наконец, подводя итог, ядром абстрактного процесса «Модели цепочки ответственности (изменения)» является:

  • Разделение по обязанностям: классификация бизнес-логики, процесс конвергенции.
  • Цепочка объектов: конвергентные бизнес-объекты формируются в цепочку объектов и выполняются последовательно.
特别说明:
1. 我的代码没有`else`,只是一个在代码合理设计的情况下自然而然无限接近或者达到的结果,并不是一个硬性的目标,务必较真。
2. 本系列的一些设计模式的概念可能和原概念存在差异,因为会结合实际使用,取其精华,适当改变,灵活使用。