go: 状态模式

2021-12-24 14:27:42 浏览数 (1)

状态模式避免了一个程序中出现大量的switch case/if。实际上是将状态的转换拆分到不同的子类中实现。 在实现子类的时候,只需要考虑当前状态,传入的条件,下一个状态。比较节省脑容量。 这是一个go实现的状态模式的例子。

代码语言:javascript复制
/* statemode 这是一个状态模式的示例
状态根据当前的信息转换
normal -心跳过期-> preOffline -继续过期-> offline
                            -心跳恢复-> normal
*/
package statemode

import (
    "fmt"
    "math"
    "time"
    "github.com/sirupsen/logrus"
)

type State interface {
    // Change 状态根据传入的info进行改变,返回新的状态或者错误
    Change(info map[string]string) (State, error)
}

type NormalState struct {
}

func (n *NormalState) Change(info map[string]string) (State, error) {
    ttime, err := datautils.ToInt(info["time"])
    if err != nil {
        return nil, err
    }
    // 判定心跳时间如果超过当前时间3秒,则状态转换为preOffline
    if abs(time.Now().Unix()-int64(ttime)) > 3 {
        return &PreOfflineState{}, nil
    }
    return n, nil
}

type PreOfflineState struct {
}

func (p *PreOfflineState) Change(info map[string]string) (State, error) {
    ttime, err := datautils.ToInt(info["time"])
    if err != nil {
        return nil, err
    }
    // 当在preOffline的时候,心跳继续过期3秒,转换为offline状态
    if abs(time.Now().Unix()-int64(ttime)) > 3 {
        return &OfflineState{}, nil
    } else {
        // 当在preOffline的时候,心跳恢复,转换为normal状态
        return &NormalState{}, nil
    }
}

type OfflineState struct {
}

func (o *OfflineState) Change(info map[string]string) (State, error) {
    // offline状态为最终状态,不能再转换
    return nil, fmt.Errorf("offline state cannot change")
}

func abs(a interface{}) float64 {
    v, err := datautils.ToFloat64(a)
    if err != nil {
        logrus.Fatal(err)
    }
    return math.Abs(v)
}

测试用例

代码语言:javascript复制
// statemode 这是一个状态模式的示例
package statemode

import (
    "reflect"
    "testing"
    "time"
    "github.com/sirupsen/logrus"
)

func TestNormalState_Change(t *testing.T) {
    initState := &NormalState{}
    normalState, err := initState.Change(map[string]string{
        "time": datautils.ToStr(time.Now().Unix()),
    })
    if err != nil {
        t.Fatal(err)
    }
    _, ok := normalState.(*NormalState)
    if !ok {
        t.Fatal()
    }

    preOfflineState, err := normalState.Change(map[string]string{
        "time": datautils.ToStr(time.Now().Unix() - 10),
    })
    if err != nil {
        t.Fatal(err)
    }
    _, ok = preOfflineState.(*PreOfflineState)
    logrus.Infof("nextState preoffline:%v", reflect.TypeOf(preOfflineState))
    if !ok {
        t.Fatal()
    }

    offlineState, err := preOfflineState.Change(map[string]string{
        "time": datautils.ToStr(time.Now().Unix() - 10),
    })
    if err != nil {
        t.Fatal(err)
    }
    _, ok = offlineState.(*OfflineState)
    logrus.Infof("nextState offline:%v", reflect.TypeOf(offlineState))
    if !ok {
        t.Fatal()
    }

    normalState, err = preOfflineState.Change(map[string]string{
        "time": datautils.ToStr(time.Now().Unix()),
    })
    if err != nil {
        t.Fatal(err)
    }
    _, ok = normalState.(*NormalState)
    logrus.Infof("nextState back to normal:%v", reflect.TypeOf(normalState))
    if !ok {
        t.Fatal()
    }
}

0 人点赞