变化多端的状态模式(State Pattern)_PHP_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > PHP > 变化多端的状态模式(State Pattern)

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

 2014/5/8 11:00:35  home198979  程序员俱乐部  我要评论(0)
  • 摘要:现在写字楼越建越高,码农上个班不但要挤个地铁,还要挤个电梯。电梯的运行简单有这么几个状态:运行、停止、关闭、打开,电梯想要正常的运行,就必须得遵循一定的规则,例如运行的时候不能开门,开门状态不能运行。按照平常的逻辑,分别创建open,close,run,stop四个方法,方法里通过switch当前的状态,执行不同的动作。这种处理有几个问题:1、扩展性太差如果电梯还有两个状态:通电状态和断电状态。那就要在open,close,run,stop四个方法里都要增加判断条件,这与开闭原则相违背。2
  • 标签:模式 变化

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

1、扩展性太差

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

2、非常规状态无法实现

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

?

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

实现类图如下:



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

class="php"><?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个。

?

?

?

?

?

发表评论
用户名: 匿名