跳到主要内容

状态模式

前言

状态模式(State Pattern)是一种行为型模式。在状态模式中,类的行为是基于它的状态改变的。状态模式允许一个对象在其内部状态发生改变时改变其行为,使这个对象看上去就像改变了它的类型一样。

设计要点

  • 在实现状态模式的时候,实现的场景状态有时候会非常复杂,决定状态变化的因素也非常多,我们可以把决定状态变化的属性单独抽象成一个类 StateInfo,这样判断状态属性是否符合当前的状态 isMatch 时就可以传入更多的信息。

优点

  • 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类中,对状态转换代码进行集中管理,而不是分散在一个个业务逻辑中。
  • 将所有与某个状态有关的行为放到一个类中(称为状态类),使开发人员只专注于该状态下的逻辑开发。
  • 允许状态转换逻辑与状态对象合为一体,使用时只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。

缺点

  • 会增加系统类和对象的个数
  • 状态模式的结构与实现都较为复杂,如果使用不当容易导致程序结构和代码的混乱。

适用场景

  • 一个对象的行为取决于它的状态, 并且它在运行时可能经常改变它的状态,从而改变它的行为。
  • 一个操作中含有庞大的多分支的条件语句,这些分支依赖于该对象的状态,且每一个分支的业务逻辑都非常复杂时,我们可以使用状态模式来拆分不同的分支逻辑,使程序有更好的可读性和可维护性。

示例代码

Go

  • state.go
package state

import "fmt"

type State interface {
Behavior(water Water) string
GetName() string
}

type SolidState struct {
Name string
}

func (s SolidState) GetName() string {
return s.Name
}

func (s SolidState) Behavior(water Water) string {
fmt.Printf("Solid State, current temperature: %.2f\n", water.GetTemperature())
return "Solid State"
}

func NewSolidState(name string) *SolidState {
return &SolidState{
Name: name,
}
}

type LiquidState struct {
Name string
}

func (s LiquidState) GetName() string {
return s.Name
}

func (s LiquidState) Behavior(water Water) string {
fmt.Printf("Liquid State, current temperature: %.2f\n", water.GetTemperature())
return "Liquid State"
}

type GasState struct {
Name string
}

func (s GasState) GetName() string {
return s.Name
}

func (s GasState) Behavior(water Water) string {
fmt.Printf("Gas State, current temperatureL %.2f\n", water.GetTemperature())
return "Gas State"
}

type Water struct {
temperature float32
state State
}

func (w *Water) setState(state State) {
w.state = state
}

func (w *Water) changeState(state State) {
if w.state != nil {
fmt.Printf("Change state from %s to %s\n", w.state.GetName(), state.GetName())
} else {
fmt.Printf("Init state to %s\n", state.GetName())
}
w.setState(state)
}

func (w *Water) GetTemperature() float32 {
return w.temperature
}

func (w *Water) SetTemperature(temperature float32) {
w.temperature = temperature

if w.temperature <= 0 {
w.changeState(SolidState{Name: "Solid"})
} else if w.temperature > 0 && w.temperature < 100 {
w.changeState(LiquidState{Name: "Liquid"})
} else {
w.changeState(GasState{Name: "Gas"})
}
}

func (w *Water) RaiseTemperature(step float32) {
w.SetTemperature(w.temperature + step)
}

func (w *Water) ReduceTemerature(step float32) {
w.SetTemperature(w.temperature - step)
}

func (w *Water) Behavior() string {
return w.state.Behavior(*w)
}

func NewWater(temperature float32, state State) *Water {
return &Water{
temperature: temperature,
state: state,
}
}
  • state_test.go
package state

import "testing"

func TestStatePattern(t *testing.T) {
water := NewWater(25, LiquidState{Name: "ye tai"})
water.Behavior()

water.RaiseTemperature(85)
if water.Behavior() != "Gas State" {
t.Fatal("Change State to Gas failed")
}

water.ReduceTemerature(20)
if water.Behavior() != "Liquid State" {
t.Fatal("Change State to Liquid failed")
}

water.ReduceTemerature(50)
if water.Behavior() != "Liquid State" {
t.Fatal("State should be liquid")
}

water.ReduceTemerature(50)
if water.Behavior() != "Solid State" {
t.Fatal("Change state to solid faild")
}
}

Python

from abc import ABCMeta, abstractmethod


class Context(metaclass=ABCMeta):
"""状态模式的上下文环境类"""

def __init__(self):
self.__states = []
self.__cur_state = None

# 状态发生变化时的属性, 当这一变量由多个变量共同决定时可以将其单独定义成一个类
self.__state_info = 0

def add_state(self, state):
if state not in self.__states:
self.__states.append(state)

def change_state(self, state):
if state is None:
return False

if self.__cur_state is None:
print(f"Init state to {state.get_name()}")
else:
print(
f"Change state from {self.__cur_state.get_name()} to {state.get_name()}"
)

self.__cur_state = state
self.add_state(state)
return True

def get_state(self):
return self.__cur_state

def _set_state_info(self, state_info):
self.__state_info = state_info
for state in self.__states:
if state.is_match(state_info):
self.change_state(state)

def _get_state_info(self):
return self.__state_info


class State:
"""状态基类"""

def __init__(self, name):
self.__name = name

def get_name(self):
return self.__name

def is_match(self, state_info):
return False

@abstractmethod
def behavior(self, context):
pass


def singleton(cls, *args, **kwargs):
instance = {}

def __singleton(*args, **kwargs):
if cls not in instance:
instance[cls] = cls(*args, **kwargs)
return instance[cls]

return __singleton


@singleton
class SolidState(State):
def __init__(self, name):
super().__init__(name)

def is_match(self, state_info):
return state_info < 0

def behavior(self, context):
print(f"Solid state, {context._get_state_info()}")


@singleton
class LiquidState(State):
def __init__(self, name):
super().__init__(name)

def is_match(self, state_info):
return state_info >= 0 and state_info < 100

def behavior(self, context):
print(f"Liquid state, {context._get_state_info()}")


@singleton
class GaseousState(State):
def __init__(self, name):
super().__init__(name)

def is_match(self, state_info):
return state_info >= 100

def behavior(self, context):
print(f"Gaseous state, {context._get_state_info()}")


class Water(Context):
def __init__(self):
super().__init__()
self.add_state(SolidState("Solid"))
self.add_state(LiquidState("Liquid"))
self.add_state(GaseousState("Gaseous"))
self.set_temperature(25)

def get_temperature(self):
return self._get_state_info()

def set_temperature(self, temperature):
self._set_state_info(temperature)

def rise_temperature(self, step):
self._set_state_info(self.get_temperature() + step)

def reduce_temperature(self, step):
self._set_state_info(self.get_temperature() - step)

def behavior(self):
state = self.get_state()
if isinstance(state, State):
state.behavior(self)


if __name__ == "__main__":
water = Water()
water.behavior()

water.set_temperature(-4)
water.behavior()

water.rise_temperature(10)
water.behavior()

water.rise_temperature(100)
water.behavior()

water.reduce_temperature(130)
water.behavior()

参考

  • 罗伟富《人人都懂设计模式》