Преобразование состояния | Шаблоны проектирования Go в действии

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

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

предисловие

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

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

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

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

«Режим состояния» относительно прост, то есть выбор алгоритма зависит от его собственного внутреннего состояния. По сравнению с выбором алгоритма «Режим стратегии» от принятия решений пользователем к принятию решений внутреннего состояния, «Режим стратегии» - это когда пользователь (клиент) выбирает конкретный алгоритм, а «Режим состояния» выбирает конкретный алгоритм только через различные внутренние состояния.

Что такое «образцовое государство»?

Разные алгоритмы упакованы по единому стандарту, по разнымвнутреннее состояние, решите, какой алгоритм использовать

Разница между «режимом состояния» и «режимом стратегии»

  • Паттерн стратегии: полагайтесь на решения клиентов
  • Режим состояния: опора на решения внутреннего состояния

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

Выбор конкретного алгоритма определяется внутренним состоянием

  • Во-первых, внутри есть несколько состояний
  • Во-вторых, бизнес-логика разных государств разная.

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

Например, интерфейс для отправки SMS-сообщений, ограничение тока и т. д.

  • SMS-интерфейс
    • В соответствии с оптимальным алгоритмом сервис внутри себя рекомендует оптимального поставщика SMS-услуг в режиме реального времени и модифицирует его.Укажите какие поставщики услуг для использования SMS
  • Ограничение
    • В соответствии с текущим трафиком в реальном времени сервис выбирает различные алгоритмы ограничения тока и модифицируетСтатус того, какой алгоритм ограничения тока используется

Как использовать «Режим статуса»?

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

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

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

Давайте сначала посмотрим на интерфейс входа с кодом подтверждения SMS.

Вы можете получить:

  • Отправляйте текстовые сообщения, пользователям нужно только ввести номер телефона
  • Что касается того, что использовать поставщика услуг обмена текстовыми сообщениями SMS, самой службой SMSСтатус текущего экземпляра провайдера SMSПринять решение
  • Статус текущего экземпляра провайдера SMSМодифицируется алгоритмом самого сервиса

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

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

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

Основной «режим состояния»:

  • Интерфейс:
    • Интерфейс смс-сервисаSmsServiceInterface
  • Класс сущности:
    • Класс объектов управления состояниемStateManager

Псевдокод выглядит следующим образом:

// 定义一个短信服务接口
- 接口`SmsServiceInterface`
	+ 抽象方法`Send(ctx *Context) error`发送短信的抽象方法

// 定义具体的短信服务实体类 实现接口`SmsServiceInterface`

- 实体类`ServiceProviderAliyun`
	+ 成员方法`Send(ctx *Context) error`具体的发送短信逻辑
- 实体类`ServiceProviderTencent`
	+ 成员方法`Send(ctx *Context) error`具体的发送短信逻辑
- 实体类`ServiceProviderYunpian`
	+ 成员方法`Send(ctx *Context) error`具体的发送短信逻辑

// 定义状态管理实体类`StateManager`
- 成员属性
	+ `currentProviderType ProviderType`当前使用的服务提供商类型
	+ `currentProvider SmsServiceInterface`当前使用的服务提供商实例
	+ `setStateDuration time.Duration`更新状态时间间隔
- 成员方法
	+ `initState(duration time.Duration)`初始化状态
	+ `setState(t time.Time)`设置状态

При этом получаем UML-диаграмму:

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

package main

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

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"
)

// Context 上下文
type Context struct {
	Tel        string // 手机号
	Text       string // 短信内容
	TemplateID string // 短信模板ID
}

// SmsServiceInterface 短信服务接口
type SmsServiceInterface interface {
	Send(ctx *Context) error
}

// ServiceProviderAliyun 阿里云
type ServiceProviderAliyun struct {
}

// Send Send
func (s *ServiceProviderAliyun) Send(ctx *Context) error {
	fmt.Println(runFuncName(), "【阿里云】短信发送成功,手机号:"+ctx.Tel)
	return nil
}

// ServiceProviderTencent 腾讯云
type ServiceProviderTencent struct {
}

// Send Send
func (s *ServiceProviderTencent) Send(ctx *Context) error {
	fmt.Println(runFuncName(), "【腾讯云】短信发送成功,手机号:"+ctx.Tel)
	return nil
}

// ServiceProviderYunpian 云片
type ServiceProviderYunpian struct {
}

// Send Send
func (s *ServiceProviderYunpian) Send(ctx *Context) error {
	fmt.Println(runFuncName(), "【云片】短信发送成功,手机号:"+ctx.Tel)
	return nil
}

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

// ProviderType 短信服务提供商类型
type ProviderType string

const (
	// ProviderTypeAliyun 阿里云
	ProviderTypeAliyun ProviderType = "aliyun"
	// ProviderTypeTencent 腾讯云
	ProviderTypeTencent ProviderType = "tencent"
	// ProviderTypeYunpian 云片
	ProviderTypeYunpian ProviderType = "yunpian"
)

var (
	// stateManagerInstance 当前使用的服务提供商实例
	// 默认aliyun
	stateManagerInstance *StateManager
)

// StateManager 状态管理
type StateManager struct {
	// CurrentProviderType 当前使用的服务提供商类型
	// 默认aliyun
	currentProviderType ProviderType

	// CurrentProvider 当前使用的服务提供商实例
	// 默认aliyun
	currentProvider SmsServiceInterface

	// 更新状态时间间隔
	setStateDuration time.Duration
}

// initState 初始化状态
func (m *StateManager) initState(duration time.Duration) {
	// 初始化
	m.setStateDuration = duration
	m.setState(time.Now())

	// 定时器更新状态
	go func() {
		for {
			// 每一段时间后根据回调的发送成功率 计算得到当前应该使用的 厂商
			select {
			case t := <-time.NewTicker(m.setStateDuration).C:
				m.setState(t)
			}
		}
	}()
}

// setState 设置状态
// 根据短信云商回调的短信发送成功率 得到下阶段发送短信使用哪个厂商的服务
func (m *StateManager) setState(t time.Time) {
	// 这里用随机模拟
	ProviderTypeArray := [3]ProviderType{
		ProviderTypeAliyun,
		ProviderTypeTencent,
		ProviderTypeYunpian,
	}
	m.currentProviderType = ProviderTypeArray[rand.Intn(len(ProviderTypeArray))]

	switch m.currentProviderType {
	case ProviderTypeAliyun:
		m.currentProvider = &ServiceProviderAliyun{}
	case ProviderTypeTencent:
		m.currentProvider = &ServiceProviderTencent{}
	case ProviderTypeYunpian:
		m.currentProvider = &ServiceProviderYunpian{}
	default:
		panic("无效的短信服务商")
	}
	fmt.Printf("时间:%s| 变更短信发送厂商为: %s \n", t.Format("2006-01-02 15:04:05"), m.currentProviderType)
}

// getState 获取当前状态
func (m *StateManager) getState() SmsServiceInterface {
	return m.currentProvider
}

// GetState 获取当前状态
func GetState() SmsServiceInterface {
	return stateManagerInstance.getState()
}

func main() {

	// 初始化状态管理
	stateManagerInstance = &StateManager{}
	stateManagerInstance.initState(300 * time.Millisecond)

	// 模拟发送短信的接口
	sendSms := func() {
		// 发送短信
		GetState().Send(&Context{
			Tel:        "+8613666666666",
			Text:       "3232",
			TemplateID: "TYSHK_01",
		})
	}

	// 模拟用户调用发送短信的接口
	sendSms()
	time.Sleep(1 * time.Second)
	sendSms()
	time.Sleep(1 * time.Second)
	sendSms()
	time.Sleep(1 * time.Second)
	sendSms()
	time.Sleep(1 * time.Second)
	sendSms()
}

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

[Running] go run "./easy-tips/go/src/patterns/state/state.go"
时间:2020-05-30 18:02:37| 变更短信发送厂商为: yunpian 
main.(*ServiceProviderYunpian).Send 【云片】短信发送成功,手机号:+8613666666666
时间:2020-05-30 18:02:37| 变更短信发送厂商为: aliyun 
时间:2020-05-30 18:02:38| 变更短信发送厂商为: yunpian 
时间:2020-05-30 18:02:38| 变更短信发送厂商为: yunpian 
main.(*ServiceProviderYunpian).Send 【云片】短信发送成功,手机号:+8613666666666
时间:2020-05-30 18:02:38| 变更短信发送厂商为: tencent 
时间:2020-05-30 18:02:39| 变更短信发送厂商为: aliyun 
时间:2020-05-30 18:02:39| 变更短信发送厂商为: tencent 
main.(*ServiceProviderTencent).Send 【腾讯云】短信发送成功,手机号:+8613666666666
时间:2020-05-30 18:02:39| 变更短信发送厂商为: yunpian 
时间:2020-05-30 18:02:40| 变更短信发送厂商为: tencent 
时间:2020-05-30 18:02:40| 变更短信发送厂商为: aliyun 
main.(*ServiceProviderAliyun).Send 【阿里云】短信发送成功,手机号:+8613666666666
时间:2020-05-30 18:02:40| 变更短信发送厂商为: yunpian 
时间:2020-05-30 18:02:40| 变更短信发送厂商为: tencent 
时间:2020-05-30 18:02:41| 变更短信发送厂商为: aliyun 
时间:2020-05-30 18:02:41| 变更短信发送厂商为: yunpian 
main.(*ServiceProviderYunpian).Send 【云片】短信发送成功,手机号:+8613666666666

Эпилог

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

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

Список статей

Go Design Pattern Combat Series Больше статей нажмите здесь, чтобы просмотреть