1.15 状态模式
状态模式(State Pattern) 采用一种结构化的方法来分割复杂系统中的行为。系统的整体行为会被分成各种定义良好的状态。通常,每个状态由一个独立的类来实现。首先,通过了解当前状态来确定整体系统行为;然后,在该状态中了解可能的行为(体现在对应此状态的类的方法中)。
1.15.1 范例
范例如下:
class Client {
def context = new Context()
def connect() {
context.state.connect()
}
def disconnect() {
context.state.disconnect()
}
def send_message(message) {
context.state.send_message(message)
}
def receive_message() {
context.state.receive_message()
}
}
class Context {
def state = new Offline(this)
}
class ClientState {
def context
ClientState(context) {
this.context = context
inform()
}
}
class Offline extends ClientState {
Offline(context) {
super(context)
}
def inform() {
println "offline"
}
def connect() {
context.state = new Online(context)
}
def disconnect() {
println "error: not connected"
}
def send_message(message) {
println "error: not connected"
}
def receive_message() {
println "error: not connected"
}
}
class Online extends ClientState {
Online(context) {
super(context)
}
def inform() {
println "connected"
}
def connect() {
println "error: already connected"
}
def disconnect() {
context.state = new Offline(context)
}
def send_message(message) {
println "\"$message\" sent"
}
def receive_message() {
println "message received"
}
}
client = new Client()
client.send_message("Hello")
client.connect()
client.send_message("Hello")
client.connect()
client.receive_message()
client.disconnect()
输出结果如下:
offline
error: not connected
connected
"Hello" sent
error: already connected
message received
offline
Groovy 这样的动态语言中有一个很棒的功能,那就是可以根据我们的需求,利用多种方式来表达该范例。下面来介绍该范例的各种变体形式。
1.15.2 变体 1:利用面向接口的设计
我们可以采取 利用面向接口的设计 的办法,为此引入下面的接口:
interface State {
def connect()
def disconnect()
def send_message(message)
def receive_message()
}
然后就可以修改 Client
、 Online
和 Offline
类来实现该接口,比如:
class Client implements State {
// ... as before ...
}
class Online implements State {
// ... as before ...
}
class Offline implements State {
// ... as before ...
}
你可能会想:我们不是刚介绍过额外的样板文件代码吗?难道不能依赖 duck-typing 吗?我们可以侥幸使用 duck-typing,但状态模式的一个关键目的在于分割复杂性。如果我们知道 client 类与每一个 state 类都满足一个接口,那么我们就能对复杂性设置一些关键限制。我们就可以独立地查看任何状态类,并且知道与该状态有关的可能行为限制。
其实不必非得使用接口来完成,但它确实能助于表达这种特殊分割方式的意图,并且有助于减少单元测试的尺寸(对于面向接口设计支持得较弱的语言,则必须添加额外的测试来表达这种意图)。
1.15.3 变体 2:抽取状态模式逻辑
作为替代(或者与其他变体相组合),我们可能抽取一部分状态模式逻辑,将其应用到辅助类中。如下例所示,可以在状态模式包/jar/脚本中定义这些类:
abstract class InstanceProvider {
static def registry = GroovySystem.metaClassRegistry
static def create(objectClass, param) {
registry.getMetaClass(objectClass).invokeConstructor([param] as Object[])
}
}
abstract class Context {
private context
protected setContext(context) {
this.context = context
}
def invokeMethod(String name, Object arg) {
context.invokeMethod(name, arg)
}
def startFrom(initialState) {
setContext(InstanceProvider.create(initialState, this))
}
}
abstract class State {
private client
State(client) { this.client = client }
def transitionTo(nextState) {
client.setContext(InstanceProvider.create(nextState, client))
}
}
它们都非常具有通用性,可以应用于任何引入状态模式的情况。下面是相应的代码:
class Client extends Context {
Client() {
startFrom(Offline)
}
}
class Offline extends State {
Offline(client) {
super(client)
println "offline"
}
def connect() {
transitionTo(Online)
}
def disconnect() {
println "error: not connected"
}
def send_message(message) {
println "error: not connected"
}
def receive_message() {
println "error: not connected"
}
}
class Online extends State {
Online(client) {
super(client)
println "connected"
}
def connect() {
println "error: already connected"
}
def disconnect() {
transitionTo(Offline)
}
def send_message(message) {
println "\"$message\" sent"
}
def receive_message() {
println "message received"
}
}
client = new Client()
client.send_message("Hello")
client.connect()
client.send_message("Hello")
client.connect()
client.receive_message()
client.disconnect()
如上所示,有了 startFrom
和 transitionTo
方法,范例开始有了点 DSL 的感觉。
1.15.4 变体 3:DSL
作为替代(或者与其他变体相组合),我们还可以全面使用领域特定语言(DSL)来处理该例。
定义下面这些通用辅助函数( 首先在此讨论 ):
class Grammar {
def fsm
def event
def fromState
def toState
Grammar(a_fsm) {
fsm = a_fsm
}
def on(a_event) {
event = a_event
this
}
def on(a_event, a_transitioner) {
on(a_event)
a_transitioner.delegate = this
a_transitioner.call()
this
}
def from(a_fromState) {
fromState = a_fromState
this
}
def to(a_toState) {
assert a_toState, "Invalid toState: $a_toState"
toState = a_toState
fsm.registerTransition(this)
this
}
def isValid() {
event && fromState && toState
}
public String toString() {
"$event: $fromState=>$toState"
}
}
class FiniteStateMachine {
def transitions = [:]
def initialState
def currentState
FiniteStateMachine(a_initialState) {
assert a_initialState, "You need to provide an initial state"
initialState = a_initialState
currentState = a_initialState
}
def record() {
Grammar.newInstance(this)
}
def reset() {
currentState = initialState
}
def isState(a_state) {
currentState == a_state
}
def registerTransition(a_grammar) {
assert a_grammar.isValid(), "Invalid transition ($a_grammar)"
def transition
def event = a_grammar.event
def fromState = a_grammar.fromState
def toState = a_grammar.toState
if (!transitions[event]) {
transitions[event] = [:]
}
transition = transitions[event]
assert !transition[fromState], "Duplicate fromState $fromState for transition $a_grammar"
transition[fromState] = toState
}
def fire(a_event) {
assert currentState, "Invalid current state '$currentState': passed into constructor"
assert transitions.containsKey(a_event), "Invalid event '$a_event', should be one of ${transitions.keySet()}"
def transition = transitions[a_event]
def nextState = transition[currentState]
assert nextState, "There is no transition from '$currentState' to any other state"
currentState = nextState
currentState
}
}
现在开始定义并测试状态机:
class StatePatternDslTest extends GroovyTestCase {
private fsm
protected void setUp() {
fsm = FiniteStateMachine.newInstance('offline')
def recorder = fsm.record()
recorder.on('connect').from('offline').to('online')
recorder.on('disconnect').from('online').to('offline')
recorder.on('send_message').from('online').to('online')
recorder.on('receive_message').from('online').to('online')
}
void testInitialState() {
assert fsm.isState('offline')
}
void testOfflineState() {
shouldFail{
fsm.fire('send_message')
}
shouldFail{
fsm.fire('receive_message')
}
shouldFail{
fsm.fire('disconnect')
}
assert 'online' == fsm.fire('connect')
}
void testOnlineState() {
fsm.fire('connect')
fsm.fire('send_message')
fsm.fire('receive_message')
shouldFail{
fsm.fire('connect')
}
assert 'offline' == fsm.fire('disconnect')
}
}
该范例与其他范例并不完全等同,没有用到预先定义的 Online
与 Offline
类,而是在需要时定义了整个状态机。关于这一方式更多精心制作的范例,可以参看 这里 。
也可以参考: 使用 ModelJUnit 的基于模型的测试 。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论