状态模式属于行为型设计模式
GoF定义:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类
没错,可以让对象的状态切换变得丝滑
场景
会员等级的降级计算
1.统计近一年内的已完成的订单,累计金额
2.然后判断金额的范围是否符合对应等级的要求,不符合则降级,符合则保级
3.若保级需要赠送保级礼包,并发送短信通知
分析 不同的等级状态,需要执行不同的业务逻辑。非常符合状态模式
1.保级时,进行数据库的会员等级更新、生成保级礼包、赠送保级礼包、发送短信通知
2.降级时,只进行数据库会员等级更新
实现
状态接口
go
// 状态接口
type ILevelState interface {
Update(user *User)
}
User
用户结构体,持有当前状态
SetLevelState
用于切换等级状态
go
type User struct {
UserInfo *UserInfo
State ILevelState
}
func (u *User) SetLevelState(afterLevel *Level) {
if u.UserInfo.LevelId > afterLevel.LevelId {
u.LevelState = NewDegradeState(u.UserInfo)
return
}
u.LevelState = NewRemainState(u.UserInfo)
}
type UserInfo struct {
UserId uint64
Nickname string
Telephone string
LevelId uint64
}
type Level struct {
Id uint64
LevelId uint64
Name string
}
降级状态
数据库更新这里就不展开说明了。
这里关键是要实现ILevelState
接口定义的方法Update(user *User)
,在这个方法里面实现自己的逻辑
go
type DegradeState struct {
UserInfo *UserInfo
}
func NewDegradeState(userInfo *UserInfo) *DegradeState {
return &DegradeState{UserInfo: userInfo}
}
func (d *DegradeState) Update(user *User) {
// 更新数据库
fmt.Printf("更新数据库, 用户ID: %v, 等级ID: %v\n", d.UserInfo.UserId, d.UserInfo.LevelId)
// 降级逻辑
fmt.Printf("用户: %v, 会员等级: %v 降级为 %v\n", d.UserInfo.Nickname, findLevel(d.UserInfo.LevelId).Name, findLevel(d.UserInfo.LevelId-1).Name)
fmt.Println()
}
保级状态
同样数据库更新、赠送礼包、发送短信这里就不展开说明了。
同样这里关键是要实现ILevelState
接口定义的方法Update(user *User)
,在这个方法里面实现自己的逻辑
go
type RemainState struct {
UserInfo *UserInfo
}
func NewRemainState(userInfo *UserInfo) *RemainState {
return &RemainState{UserInfo: userInfo}
}
func (r *RemainState) Update(user *User) {
// 更新数据库
fmt.Printf("更新数据库, 用户ID: %v, 等级ID: %v\n", r.UserInfo.UserId, r.UserInfo.LevelId)
// 保级逻辑
fmt.Printf("赠送保级礼包,发送短信通知, 用户ID: %v, 手机号:%v\n", r.UserInfo.UserId, r.UserInfo.Telephone)
fmt.Printf("用户: %v, 保级成功, 会员等级: %v\n", r.UserInfo.Nickname, findLevel(r.UserInfo.LevelId).Name)
fmt.Println()
}
等级统计
等级统计完成后,用SetLevelState
进行状态切换,随后执行状态自己的更新逻辑。
让不同状态更新逻辑解耦。
go
// LevelStatistic 等级统计
type LevelStatistic struct{}
func NewLevelStatistic() *LevelStatistic {
return &LevelStatistic{}
}
// 计算用户等级并更新状态
func (c *LevelStatistic) Calculate(user *User) {
// 模拟计算逻辑
// 实际应根据具体要求进行计算,比如:统计近一年内的已完成的订单,累计金额,然后判断范围
// 模拟计算,获取随机值,可能会出现升级的情况,这里不考虑,实际是不会出现的
levels := getAllLevel()
rand.Seed(time.Now().UnixNano())
n := rand.Intn(4)
afterLevel := levels[n]
// 更改用户状态
user.SetLevelState(afterLevel)
// 执行当前状态的逻辑
user.LevelState.Update(user)
}
完整代码
go
package program
import (
"fmt"
"math/rand"
"time"
)
// 用户结构体,持有当前状态
type User struct {
UserInfo *UserInfo
LevelState ILevelState
}
func (u *User) SetLevelState(afterLevel *Level) {
if u.UserInfo.LevelId > afterLevel.LevelId {
u.LevelState = NewDegradeState(u.UserInfo)
return
}
u.LevelState = NewRemainState(u.UserInfo)
}
type UserInfo struct {
UserId uint64
Nickname string
Telephone string
LevelId uint64
}
type Level struct {
Id uint64
LevelId uint64
Name string
}
// 获取所有等级
func getAllLevel() []*Level {
// 模拟数据库返回
return []*Level{
{Id: 10, LevelId: 1, Name: "粉丝"},
{Id: 9, LevelId: 2, Name: "黄金"},
{Id: 20, LevelId: 3, Name: "铂金"},
{Id: 18, LevelId: 4, Name: "钻石"},
}
}
// 查找等级
func findLevel(levelId uint64) *Level {
// 模拟数据库返回
levels := getAllLevel()
for _, item := range levels {
if levelId == item.LevelId {
return item
}
}
return nil
}
// 状态接口
type ILevelState interface {
Update(user *User)
}
// 降级状态
type DegradeState struct {
UserInfo *UserInfo
}
func NewDegradeState(userInfo *UserInfo) *DegradeState {
return &DegradeState{UserInfo: userInfo}
}
func (d *DegradeState) Update(user *User) {
// 更新数据库
fmt.Printf("更新数据库, 用户ID: %v, 等级ID: %v\n", d.UserInfo.UserId, d.UserInfo.LevelId)
// 降级逻辑
fmt.Printf("用户: %v, 会员等级: %v 降级为 %v\n", d.UserInfo.Nickname, findLevel(d.UserInfo.LevelId).Name, findLevel(d.UserInfo.LevelId-1).Name)
fmt.Println()
}
// 保级状态
type RemainState struct {
UserInfo *UserInfo
}
func NewRemainState(userInfo *UserInfo) *RemainState {
return &RemainState{UserInfo: userInfo}
}
func (r *RemainState) Update(user *User) {
// 更新数据库
fmt.Printf("更新数据库, 用户ID: %v, 等级ID: %v\n", r.UserInfo.UserId, r.UserInfo.LevelId)
// 保级逻辑
fmt.Printf("赠送保级礼包,发送短信通知, 用户ID: %v, 手机号:%v\n", r.UserInfo.UserId, r.UserInfo.Telephone)
fmt.Printf("用户: %v, 保级成功, 会员等级: %v\n", r.UserInfo.Nickname, findLevel(r.UserInfo.LevelId).Name)
fmt.Println()
}
// LevelStatistic 等级统计
type LevelStatistic struct{}
func NewLevelStatistic() *LevelStatistic {
return &LevelStatistic{}
}
// 计算用户等级并更新状态
func (c *LevelStatistic) Calculate(user *User) {
// 模拟计算逻辑
// 实际应根据具体要求进行计算,比如:统计近一年内的已完成的订单,累计金额,然后判断范围
// 模拟计算,获取随机值,可能会出现升级的情况,这里不考虑,实际是不会出现的
levels := getAllLevel()
rand.Seed(time.Now().UnixNano())
n := rand.Intn(4)
afterLevel := levels[n]
// 更改用户状态
user.SetLevelState(afterLevel)
// 执行当前状态的逻辑
user.LevelState.Update(user)
}
func main() {
users := []*UserInfo{
{UserId: 1, Nickname: "测试1", Telephone: "13999999999", LevelId: 3},
{UserId: 2, Nickname: "测试2", Telephone: "13999999998", LevelId: 3},
{UserId: 3, Nickname: "测试3", Telephone: "13999999997", LevelId: 4},
}
statistic := NewLevelStatistic()
for _, userInfo := range users {
user := &User{
UserInfo: userInfo,
LevelState: NewRemainState(userInfo), // 初始状态为保级状态
}
statistic.Calculate(user)
}
}
总结
状态模式将对象的每个状态封装为独立的类,并通过状态对象的切换来改变对象的行为,从而实现对象在不同状态下的不同行为。
这种模式使得代码更加清晰、模块化,并且易于扩展。
体现了对扩展开放,对修改关闭 、单一职责 设计原则,让状态切换变得如此丝滑
这里也许有的小伙伴会想,观察者不是也可以实现么?
的确,不否认,观察者也可以实现。
但观察者还可以是对象状态变化通知其他一个或多个对象进行逻辑处理;
状态模式是对象状态变化通知自己进行逻辑处理。
看到这里,我想你应该了解了状态模式。
快看看自己的项目代码,是不是也可以使用状态模式吧!