`

变化多端的状态模式(State Pattern)

阅读更多

现在写字楼越建越高,码农上个班不但要挤个地铁,还要挤个电梯。电梯的运行简单有这么几个状态:运行、停止、关闭、打开,电梯想要正常的运行,就必须得遵循一定的规则,例如运行的时候不能开门,开门状态不能运行。按照平常的逻辑,分别创建open,close,run,stop四个方法,方法里通过switch当前的状态,执行不同的动作。这种处理有几个问题:

1、扩展性太差

如果电梯还有两个状态:通电状态和断电状态。那就要在open,close,run,stop四个方法里都要增加判断条件,这与开闭原则相违背。

2、非常规状态无法实现

电梯在门开着的状态下就不能上下运行了吗?电梯有没有发生过只有运行没有停止状态呢?电梯故障嘛。还有电梯在检修的时候,可以在stop状态下不开门,这也是正常的业务需求啊。如果加上这些需求,又有多少程序要改动?

 

既然平常的办法会带来这么多问题,当然要找好的模式来解决——状态模式

实现类图如下:



在类图中,定义了一个ListState抽象类,声明了一个受保护的类型Content变量,这个是串联各个状态的封装类。封装的目的很明显,就是电梯对象内部状态的变化不被调用类知晓,也就是迪米特法则了,并且还定义了四个具体的实现类,承担的是状态的产生以及状态间的转换过渡。具体实现代码:

<?php
abstract class LiftState {
	protected $content;
	public function setContent( Content $content ) {
		$this->content =  $content;
	}
	public abstract function open();
	public abstract function close();
	public abstract function run();
	public abstract function stop();
}

class OpenningState extends LiftState{
	public function close() {
		$this->content->setLiftState( $this->content->closingState );
		$this->content->getLiftState()->close();
	}
	public function open() {
		echo "电梯门开启\n";
	}
	public function run() {}
	public function stop() {}
}

class ClosingState extends LiftState{
	public function close() {
		echo "电梯门关闭\n";
	}
	public function open() {
		$this->content->setLiftState( $this->content->openningState );
		$this->content->getLiftState()->open();
	}
	public function run() {
		$this->content->setLiftState( $this->content->runningState );
		$this->content->getLiftState()->run();
	}
	public function stop() {
		$this->content->setLiftState( $this->content->stoppingState );
		$this->content->getLiftState()->stop();
	}
}

class RunningState extends LiftState{
	public function close() {}
	public function open() {}
	public function run() {
		echo "电梯开动了\n";
	}
	public function stop() {
		$this->content->setLiftState( $this->content->stoppingState );
		$this->content->getLiftState()->stop();
	}
}

class StoppingState extends LiftState{
	public function close() {}
	public function open() {
		$this->content->setLiftState( $this->content->openningState );
		$this->content->getLiftState()->open();
	}
	public function run() {
		$this->content->setLiftState( $this->content->runningState );
		$this->content->getLiftState()->run();
	}
	public function stop() {
		echo "电梯停止了\n";
	}
}

class Content {
	public $openningState;
	public $runningState;
	public $closingState;
	public $stoppingState;
	private $liftState;
	public function __construct() {
		$this->openningState = new OpenningState();
		$this->runningState = new RunningState();
		$this->closingState = new ClosingState();
		$this->stoppingState = new StoppingState();
	}
	public function getLiftState() {
		return $this->liftState;
	}
	public function setLiftState( LiftState $liftState ) {
		$this->liftState = $liftState;
		$this->liftState->setContent( $this );
	}
	public function open() {
		$this->liftState->open();
	}
	public function close() {
		$this->liftState->close();
	}
	public function run() {
		$this->liftState->run();
	}
	public function stop() {
		$this->liftState->stop();
	}
}

$content = new Content();
$content->setLiftState( new ClosingState() );
$content->open();
$content->close();
$content->run();
$content->stop();
$content->close();
?>
运行结果:
电梯门开启
电梯门关闭
电梯开动了
电梯停止了
[Finished in 0.1s]

 

 

状态模式的定义

当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。看看状态模式的三个角色:

1、State——抽象状态角色

接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态切换

2、ConcreteState——具体状态角色

每一个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理,通俗地说,就是本状态下要做的事情,以及本状态如何过渡到其他状态

3、Content——环境角色

定义客户端需要的接口,并且负责具体状态的切换

 

状态模式相对比较复杂,它提供了一种对物质运动的另一个观察视角,通过状态变更促使行为的变化,就类似水的状态变更一样,一碗水的初始状态是液态,通过加热转变为气态,状态的改变同时也引起体积的扩大,然后就产生了一个新的行为。

 

 

状态模式的优点

1、结构清晰

避免了过多的swith...case或if...else语句的使用,避免了程序的复杂性,提高系统的可维护性

2、遵循设计原则

很好地体现了开闭原则和单一职责原则,每一个状态都是一个子类,你要增加状态就增加子类,你要修改状态,你只修改一个子类就可以了。

3、封装性非常好

这也是状态模式的基本要求,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。

 

 

状态模式的缺点

子类会太多,也就是类膨胀,不好管理。

 

 

状态模式的使用场景

1、行为随状态改变而改变的场景

这也是状态模式的根本出发点,例如权限设计,人员的状态不同即使执行相同的行为结果也会不同,在这种情况下需要考虑使用状态模式

2、条件、分支判断语句的替代者

在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,它通过扩展子类实现了条件的判断处理

 

 

状态模式的注意事项

状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说在行为受状态约束的情况下可以使用状态模式,而且使用时对象的状态最好不要超过5个。

 

 

 

 

 

2
1
分享到:
评论
4 楼 houxinyou 2014-05-08  
201403262452 写道
houxinyou 写道
程序结构很好,看了之后给人感觉非常清晰,但是很好地体现了开闭原则这个有点不太认同,如果增加了一种状态,必然会改变抽象类,在里面增加对应的状态方法,那么实现类里也必然要实现对应的接口方法,也就是说,增加一种状态,在增加一个状态类的,其它状态类都要改变

看的挺仔细的哈。不过如果增加一种状态,抽象类和实现类本身的方法是不需要更改的,只需要增加方法,这一点是符合开闭原则的。而并不是说整个类不作任何修改。

可能是我对开闭原则理解的不够,但我感觉应该是增加一个状态,不需要修改现有的状态类才好,但是我也没想到怎么样做才能够做到,不过现在的实现结构也很清晰了
3 楼 201403262452 2014-05-08  
houxinyou 写道
程序结构很好,看了之后给人感觉非常清晰,但是很好地体现了开闭原则这个有点不太认同,如果增加了一种状态,必然会改变抽象类,在里面增加对应的状态方法,那么实现类里也必然要实现对应的接口方法,也就是说,增加一种状态,在增加一个状态类的,其它状态类都要改变

看的挺仔细的哈。不过如果增加一种状态,抽象类和实现类本身的方法是不需要更改的,只需要增加方法,这一点是符合开闭原则的。而并不是说整个类不作任何修改。
2 楼 houxinyou 2014-05-08  
就需求来说,感觉不应该简单的给电梯定义一个状态变量,而是应该给电梯定义两个状态变量:电梯门的状态和电梯的运行状态,在电梯门的状态方法里对电梯运行状态进行判断,当电梯运行状态为运行时,电梯门状态不允许改变,反之在电梯运行状态方法里进行判断,当电梯门为打开时,电梯的运行状态不允许改变
1 楼 houxinyou 2014-05-08  
程序结构很好,看了之后给人感觉非常清晰,但是很好地体现了开闭原则这个有点不太认同,如果增加了一种状态,必然会改变抽象类,在里面增加对应的状态方法,那么实现类里也必然要实现对应的接口方法,也就是说,增加一种状态,在增加一个状态类的,其它状态类都要改变

相关推荐

    [源代码] 《易学 设计模式》 随书源代码

    第18章 变化多端:状态模式 (State) 第19章 明修栈道,暗度陈仓:策略模式 (Strategy) 第20章 循序渐进:职责链模式 (ChainofResponsibility) 第21章 独具匠心:命令模式 (Command) 第22章 步调一致:访问者模式 ...

    C#面向对象设计模式纵横谈(25):设计模式总结

    - **State**模式允许对象在其内部状态改变时改变其行为。对象看起来像是改变了其类。 - **Memento**模式在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复...

    小学语文近义词变化多端的近义词

    9. 摇身一变:形象地表示短时间内身份或状态的巨大转变,也反映了变化的快速和显著。 10. 千变万化:形容事物变化复杂,多样且快速,与"变化多端"的含义相吻合。 11. 波云诡谲:比喻局势复杂多变,难以捉摸,常用于...

    avascript设计模式.pdf

    在JavaScript中使用设计模式可以提升代码的效率和可处理性,有助于应对变化多端的编程挑战。 #### 5. JavaScript的灵活性 JavaScript的灵活性体现在它既可以写得非常简单,也可以通过复杂的编程风格实现功能。其...

    发现模式讲义.rar

    发现模式是软件设计模式中的一种,它关注于如何在软件中找到并表示对象之间的关系,以便更好地理解和操作这些关系。...通过对这个主题的深入学习,开发者将能更好地处理变化多端的软件需求和复杂系统中的对象交互。

    项目百态_深入理解软件项目行为模式

    每个项目都有其独特的生命周期,从初期的规划到最终的交付,过程中可能遇到的问题和变化多端。作者通过深入分析不同项目阶段的行为模式,提供了对项目管理有深远启示的洞察。 其次,"深入理解软件项目行为模式"意味...

    变化多端的云作文.doc

    【变化多端的云】是自然界中的一种奇妙现象,它以无尽的形态吸引着人们的目光,成为众多艺术家和诗人灵感的源泉。云是由水蒸气在大气中冷却后凝结形成的,其形状和结构的变化反映了大气的动态过程,包括温度、湿度、...

    android 打造变化多端的SeekBar(垂直和水平)

    android 打造变化多端的SeekBar(垂直和水平)。压缩包里面有三个android项目源码。都是SeekBar相关。垂直,水平的都有。我博客地址:http://blog.csdn.net/qq_16064871。

    基于获取持续竞争优势的科技成果转化知识学习模式

    在当前的科技创新背景下,科技成果转化已经成为了推动社会和经济发展的重要力量。然而,科技成果的转化并非易事,...通过深入分析和实施这些模式,组织能够更好地适应变化多端的市场环境,持续推动科技成果的成功转化。

    B2C电商供应链多主体配送模式研究.pdf

    1. 需求不确定性:消费者需求变化多端,难以预测,这对供应链的应变能力提出了较高的要求。 2. 产业边界可渗透性:随着技术的发展,各产业之间的界限变得模糊,跨界竞争成为常态。 3. 供应链协同效应差异性:不同...

    PRML_中译版.pdf马春鹏

    这不是⼀个简单的问题,因为⼿写体变化多端。这个问题可以使⽤⼈⼯编 写的规则解决,或者依据笔画的形状启发式地区分数字,但是实际中这样的⽅法导致了规则数 量的激增,以及不符合规则的例外等等,并且始终给出较差...

    信息抽取sftmealy详细算法.pdf

    本文主要讨论了一种基于有限状态转换器(Finite-State Transducers, FST)的信息抽取算法,该算法应用于半结构化文本的挖掘,如网页中的表格和列表数据。以下是对"信息抽取sftmealy详细算法.pdf"文档中提到的知识点...

    分布式能源模式下电能计量装置需求探讨.pdf

    分布式能源系统的计量特点决定了电能计量装置必须能够适应变化多端的发电和用电模式。例如,分布式能源如风电、太阳能发电的输出负荷具有随机性和不稳定性,受环境、季节、地域等因素影响较大。此外,负荷潮流可能...

    具有随机变化的非线性和传感器故障的复杂网络的状态估计

    尤其在复杂网络变化多端的环境中,非线性特性更是增加了研究的复杂度。在面对具有随机变化的非线性和传感器故障的复杂网络时,研究者需要设计状态估计器来保证系统的稳定性和性能。 具体而言,研究团队采用了伯努利...

Global site tag (gtag.js) - Google Analytics