在Golang的业务开发中,状态模式和策略模式都是行为型设计模式的重要成员,二者结合使用可以很好地应对对象状态多变且不同状态下存在多种处理逻辑的场景。状态模式关注对象内部状态变化带来的行为改变,策略模式则聚焦于不同算法的封装与动态切换,将二者结合可以兼顾状态管理和算法灵活性。

两种模式的核心概念
状态模式
状态模式允许一个对象在其内部状态改变时改变它的行为,对象看起来好像修改了它的类。核心是将状态相关的行为封装到独立的状态类中,当对象状态切换时,切换对应的状态类实例即可。
策略模式
策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户而变化,核心是通过接口定义算法族,运行时动态选择具体策略。
结合场景设计
我们以订单处理系统为例,订单存在待支付、已支付、待发货、已发货、已完成、已取消等多种状态,不同状态下订单的处理逻辑不同,同时部分状态下存在多种可选的处理策略,比如待支付状态下可以使用微信支付、支付宝支付等不同支付策略。
接口定义
首先定义状态接口和策略接口:
// 订单状态接口
type OrderState interface {
// 处理订单
HandleOrder(order *Order) error
// 获取状态名称
GetStateName() string
}
// 支付策略接口
type PayStrategy interface {
// 执行支付
Pay(amount float64) error
// 获取策略名称
GetStrategyName() string
}
具体状态实现
实现待支付状态,该状态下需要选择支付策略完成支付:
// 待支付状态
type WaitPayState struct {
payStrategy PayStrategy
}
func (w *WaitPayState) SetPayStrategy(strategy PayStrategy) {
w.payStrategy = strategy
}
func (w *WaitPayState) HandleOrder(order *Order) error {
if w.payStrategy == nil {
return fmt.Errorf("未设置支付策略")
}
err := w.payStrategy.Pay(order.Amount)
if err != nil {
return err
}
// 支付成功后切换到已支付状态
order.SetState(&PaidState{})
return nil
}
func (w *WaitPayState) GetStateName() string {
return "待支付"
}
// 已支付状态
type PaidState struct{}
func (p *PaidState) HandleOrder(order *Order) error {
// 已支付状态下触发发货逻辑
order.SetState(&WaitDeliverState{})
return nil
}
func (p *PaidState) GetStateName() string {
return "已支付"
}
// 待发货状态
type WaitDeliverState struct{}
func (w *WaitDeliverState) HandleOrder(order *Order) error {
// 执行发货逻辑
order.SetState(&DeliveredState{})
return nil
}
func (w *WaitDeliverState) GetStateName() string {
return "待发货"
}
// 已发货状态
type DeliveredState struct{}
func (w *DeliveredState) HandleOrder(order *Order) error {
// 确认收货后切换到已完成状态
order.SetState(&FinishedState{})
return nil
}
func (w *DeliveredState) GetStateName() string {
return "已发货"
}
// 已完成状态
type FinishedState struct{}
func (w *FinishedState) HandleOrder(order *Order) error {
return fmt.Errorf("订单已完成,无法处理")
}
func (w *FinishedState) GetStateName() string {
return "已完成"
}
// 已取消状态
type CanceledState struct{}
func (w *CanceledState) HandleOrder(order *Order) error {
return fmt.Errorf("订单已取消,无法处理")
}
func (w *CanceledState) GetStateName() string {
return "已取消"
}
具体策略实现
实现两种支付策略:
// 微信支付策略
type WechatPayStrategy struct{}
func (w *WechatPayStrategy) Pay(amount float64) error {
// 模拟微信支付逻辑
fmt.Printf("使用微信支付完成支付,金额:%.2f元n", amount)
return nil
}
func (w *WechatPayStrategy) GetStrategyName() string {
return "微信支付"
}
// 支付宝支付策略
type AliPayStrategy struct{}
func (a *AliPayStrategy) Pay(amount float64) error {
// 模拟支付宝支付逻辑
fmt.Printf("使用支付宝支付完成支付,金额:%.2f元n", amount)
return nil
}
func (a *AliPayStrategy) GetStrategyName() string {
return "支付宝支付"
}
订单上下文实现
订单结构体作为上下文,持当前状态和订单信息:
type Order struct {
Id string
Amount float64
state OrderState
}
func NewOrder(id string, amount float64) *Order {
return &Order{
Id: id,
Amount: amount,
state: &WaitPayState{},
}
}
func (o *Order) SetState(state OrderState) {
o.state = state
}
func (o *Order) GetStateName() string {
return o.state.GetStateName()
}
func (o *Order) Handle() error {
return o.state.HandleOrder(o)
}
// 待支付状态下设置支付策略的方法
func (o *Order) SetPayStrategy(strategy PayStrategy) {
if waitPayState, ok := o.state.(*WaitPayState); ok {
waitPayState.SetPayStrategy(strategy)
}
}
组合使用演示
编写测试代码验证两种模式的结合效果:
func main() {
// 创建订单
order := NewOrder("ORD001", 199.99)
fmt.Printf("当前订单状态:%sn", order.GetStateName())
// 设置微信支付策略
order.SetPayStrategy(&WechatPayStrategy{})
// 处理订单(支付)
err := order.Handle()
if err != nil {
fmt.Printf("处理失败:%vn", err)
return
}
fmt.Printf("当前订单状态:%sn", order.GetStateName())
// 继续处理订单(发货)
err = order.Handle()
if err != nil {
fmt.Printf("处理失败:%vn", err)
return
}
fmt.Printf("当前订单状态:%sn", order.GetStateName())
// 继续处理订单(确认收货)
err = order.Handle()
if err != nil {
fmt.Printf("处理失败:%vn", err)
return
}
fmt.Printf("当前订单状态:%sn", order.GetStateName())
// 已完成状态下再次处理
err = order.Handle()
if err != nil {
fmt.Printf("处理失败:%vn", err)
}
}
实践总结
通过上面的实现可以看到,状态模式负责订单状态的流转和不同状态下的行为封装,策略模式负责待支付状态下不同支付方式的算法封装,二者结合既保证了状态管理的清晰性,又保证了支付逻辑的扩展性。如果后续需要新增支付方式,只需要新增策略实现即可,不需要修改状态类的代码;如果需要新增订单状态,只需要新增状态实现并调整状态流转逻辑即可,符合开闭原则。