
这里对需求进行了简化,实际上真实的业务需求每个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 ni, 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 年 4 月 18 日 看着有比较明显的范式:满足 condition 1 执行 action 1 。 做一下接口抽象,责任链来编排一下,具体的编排通过配置文件来实现,交给产品、业务自己去配置,不要 hardcode 翻译到代码里面。 |