这里对需求进行了简化,实际上真实的业务需求每个Condition
包含非常复杂的逻辑,如果全都用 if 嵌套来实现,只能拆分成一个一个的方法
后来想到能否直接表示出整个链条,就改写成了下面的样子,求各位大佬帮忙看下这种写法的优缺点, 如果有更好的方法, 也请大佬们指点一二
package main
import (
"log"
)
type ConditionUnitItf interface {
Condition(interface{}) (bool, error)
}
type ConditionUnitWrapper struct {
Params interface{}
Result interface{}
ConditionUnit ConditionUnitItf
TrueHandler *ConditionUnitWrapper
FalseHandler *ConditionUnitWrapper
}
// NewUnit ...
func NewUnit(unit ConditionUnitItf) *ConditionUnitWrapper {
return &ConditionUnitWrapper{
ConditionUnit: unit,
}
}
// True 设置这个实例的下一个 handler
func (s *ConditionUnitWrapper) True(nextHandler *ConditionUnitWrapper) *ConditionUnitWrapper {
s.TrueHandler = nextHandler.WithParams(s.Params)
return s
}
func (s *ConditionUnitWrapper) False(nextHandler *ConditionUnitWrapper) *ConditionUnitWrapper {
s.FalseHandler = nextHandler
return s
}
// WithParams 给实例设置参数
func (s *ConditionUnitWrapper) WithParams(params interface{}) *ConditionUnitWrapper {
s.Params = params
return s
}
func (s *ConditionUnitWrapper) SetResult(r interface{}) *ConditionUnitWrapper {
s.Result = r
return s
}
// Run 构造 chain 的核心逻辑,除了叶子节点,其他的 Condition Unit 都使用了 Condition Base 的 Run 方法
func (s *ConditionUnitWrapper) Run() (*ConditionUnitWrapper, error) {
isTrue, err := s.ConditionUnit.Condition(s.Params)
if err != nil {
return nil, err
}
if isTrue && s.TrueHandler != nil {
// 这里使用 WithParams 将参数传递给 handler
return s.TrueHandler.WithParams(s.Params).Run()
}
if !isTrue && s.FalseHandler != nil {
return s.FalseHandler.WithParams(s.Params).Run()
}
return s, nil
}
// ===================== 上面是实现了,下面要实现多个 条件单元
type userParams struct {
IsInCompany bool // 工作时间是否小于 10 小时
Destination string // 目的地是 家 还是 餐厅
IsNeedWorkAtHome bool // 是否在家要工作
IsHungry bool // 是否饿了
LastAction int // 最后的结果行为
}
// 定义 3 个行为
const (
HumanActionWork = iota // 工作
HumanActionEating // 吃饭
HumanActionRest // 休息
)
// 下面我要构造条件链路,按照当前的 user 状态(userParams) 来计算下一步的行为
// 条件链路包含的单元为
// 1. 是否在公司
// 2. 是否回家了
// 3. 是否饿了
// 4. 是否继续工作
// 上面出现了 4 个条件判断,所以下面我要创建四个条件单元
// 是否下班
type UnitIsInCompany struct {
}
// 实际的代码逻辑中,每一个 Condition 可能会包含更加复杂的逻辑
func (s UnitIsInCompany) Condition(params interface{}) (bool, error) {
return params.(userParams).IsInCompany, nil
}
// 是否回家
type UnitIsDestinationHome struct {
}
func (s UnitIsDestinationHome) Condition(params interface{}) (bool, error) {
return params.(userParams).Destination == "home", nil
}
// 是否饿了
type UnitIsHungry struct {
}
func (s UnitIsHungry) Condition(params interface{}) (bool, error) {
return params.(userParams).IsHungry, nil
}
// 是否需要继续工作
type UnitIsNeedWorkAtHome struct {
}
func (s UnitIsNeedWorkAtHome) Condition(params interface{}) (bool, error) {
if params.(userParams).IsNeedWorkAtHome {
return true, nil
}
return false, nil
}
// ===================== 下面还要针对条件写叶子结点的结构
// 对于叶子节点而言, 主要是用于收尾,调用 SetResult
type LeafUnitEchoAction struct {
}
func (s LeafUnitEchoAction) Condition(params interface{}) (bool, error) {
return true, nil
}
func main() {
user := userParams{
IsInCompany: false,
Destination: "home",
IsNeedWorkAtHome: false,
IsHungry: false,
}
// 按照条件构造的链路为如下:
obj, err := NewUnit(UnitIsInCompany{}).WithParams(user).
// 如果在公司,就工作
True(NewUnit(LeafUnitEchoAction{}).SetResult(HumanActionWork)).
// 如果不在公司,判断目的地是否是 Home
False(
NewUnit(UnitIsDestinationHome{}).
// 回家后是否需要继续工作
True(
NewUnit(UnitIsNeedWorkAtHome{}).
// 继续工作
True(NewUnit(LeafUnitEchoAction{}).SetResult(HumanActionWork)).
// 不工作就休息
False(NewUnit(LeafUnitEchoAction{}).SetResult(HumanActionRest))).
// 不回家,是否饿了
False(NewUnit(UnitIsHungry{}).
// 饿了就去餐厅吃饭
True(NewUnit(LeafUnitEchoAction{}).SetResult(HumanActionEating)).
// 不饿就休息
False(NewUnit(LeafUnitEchoAction{}).SetResult(HumanActionRest)))).
Run()
if err != nil {
log.Fatalf("err is %v", err)
}
// 最后通过 GetParams 获取到 LastAction
log.Printf("action is %v", obj.Result)
}
1
sunshinev OP 还有这种,是不是降低了圈复杂度。。。。
|
2
mrgeneral 2023-04-18 11:50:53 +08:00
看着有比较明显的范式:满足 condition 1 执行 action 1 。
做一下接口抽象,责任链来编排一下,具体的编排通过配置文件来实现,交给产品、业务自己去配置,不要 hardcode 翻译到代码里面。 |