个人理解
定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变其类。
理解:这个模式主要解决的问题是消除庞大的条件分之语句,条件分支语句过大的使用,可能出现维护困难,操控的对象方法较为复杂等问题。例如订单的状态、游戏的等级等等都可以使用状态模式。
主要解决:对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为(状态模式的行为上说的,如果从状态模式的表现上来看,还是用于消除庞大的条件分之语句)。
组成部分:一个状态控制抽象类(State)、若干个状态控制类(StateA、StateB...)、一个主题对象(Context)。
案例分析
设计场景
某卡牌游戏平台,每个紫色英雄可以进阶5次:青铜(初始)、白银、黄金、铂金、钻石、大师(实在找不到进阶的名称了,将就着看吧,以下统称“段位”),每个阶段分别存在最大等级上限(30、40、50、60、70、80)。每次升级后,等级重置。这种情况下,数据库存储可以分为两种方式,第一种:设置两个字段,一个字段为用户等级,另一个字段为用户的段位等级;另一种:设置一个字段,值为上一段位最大等级数值加当前阶段等级,例如:字段存储值(以下统称“存储值”)为88,则该英雄为黄金18级。下面的例子将以第二种为例。
面对过程想法
将每个段位的等级上限设置为一个数组,便利数组,判断英雄的存储值是否小于每个元素,如果大于则该英雄的段位就是当前段位,等级为英雄的存储值减去上一个段位的最大等级。
问题出现于,当游戏平台大多数玩家都达到满级了,玩家流失严重,需要升级游戏。现在增加橙色英雄,可以进阶6次:青铜(初始)、白银、黄金、铂金、钻石、大师、王者,每个阶段分别存在最大等级上限(30、40、50、60、70、80)。这可怎么改,在外面在增加一个判断,如果是紫色英雄...,如果是橙色英雄将上面那个复制下来,代码冗余不说,修改还贼费劲,吃力不讨好。
面对对象想法
增加一个英雄抽象类,生成两个实体英雄类:橙色英雄、紫色英雄,两个类的获取等级的方法不一样就可以了。目前存在的问题还是存在。根据单一职责定义:“就一个类而言,应该仅有一个引起它变化的原因。”在这个类中尽量少的增加其他功能,我们是不是可以将英雄的状态控制认为成一个功能。另外,由于存在庞大的条件分之语句,我们在修改的时候还是不方便。
使用状态模式
建立一个状态控制抽象类、六个具体的状态控制类、一个英雄抽象类、两个具体英雄类,具体的状态控制类继承状态控制抽象类,实现与英雄类相关的状态方法,两个具体的英雄类继承英雄抽象类,然后每个英雄都操作具体的功能。如果需要在增加一种英雄,只需要增加一个英雄类就可以了;如果需要在增加一种状态(例如“青铜”与“白银”之间增加“铝合金”段位),直接增加一个状态类,然后在修改上下两个段位的方法就好了,再也不用担心维护麻烦了。
场景代码
英雄抽象类:
<?php
//英雄抽象类
abstract class Hero{
protected $hp; //血量
protected $mp; //魔法值
protected $atk; //攻击力
protected $type; //当前英雄的类型
//获取当前英雄的等级
abstract public function getState($level);
}
橙色英雄:
<?php
//橙色英雄
class OrangeHero extends Hero
{
protected $hp;
protected $mp;
protected $atk;
protected $type = 2;
public function getState($level)
{
$state = new BronzeState();
return $state->getState($level, $this->type);
}
}
紫色英雄:
<?php
//紫色英雄
class PurpleHero extends Hero
{
protected $hp;
protected $mp;
protected $atk;
protected $type = 1;
public function getState($level)
{
//获取当前英雄的等级值
$state = new BronzeState();
return $state->getState($level, $this->type);
}
}
状态抽象类:
<?php
//状态抽象类
abstract class State
{
protected $maxLevel; //当前状态等级上限
protected $name; //当前状态名称
abstract public function getState($level, $type);
}
青铜状态类:
<?php
//青铜
class BronzeState extends State
{
protected $maxLevel = 30;
protected $name = '青铜';
public function getState($level, $type)
{
$nextStateLevel = $level-$this->maxLevel;
if ($level>$this->maxLevel) {
$state = new SilverState();
return $state->getState($nextStateLevel, $type);
} else {
return [
'state' => $this->name,
'level' => $level
];
}
}
}
白银状态类:
<?php
//白银
class SilverState extends State
{
protected $maxLevel = 40;
protected $name = '白银';
public function getState($level, $type)
{
$nextStateLevel = $level-$this->maxLevel;
if ($level>$this->maxLevel) {
$state = new GoldState();
return $state->getState($nextStateLevel, $type);
} else {
return [
'state' => $this->name,
'level' => $level
];
}
}
}
黄金状态类:
<?php
//黄金
class GoldState extends State
{
protected $maxLevel = 50;
protected $name = '黄金';
public function getState($level, $type)
{
$nextStateLevel = $level-$this->maxLevel;
if ($level>$this->maxLevel) {
$state = new PlatinumState();
return $state->getState($nextStateLevel, $type);
} else {
return [
'state' => $this->name,
'level' => $level
];
}
}
}
铂金状态类:
<?php
//铂金
class PlatinumState extends State
{
protected $maxLevel = 60;
protected $name = '铂金';
public function getState($level, $type)
{
$nextStateLevel = $level-$this->maxLevel;
if ($level>$this->maxLevel) {
$state = new GoldState();
return $state->getState($nextStateLevel, $type);
} else {
return [
'state' => $this->name,
'level' => $level
];
}
}
}
钻石状态类:
<?php
//钻石
class DiamondState extends State
{
protected $maxLevel = 70;
protected $name = '钻石';
public function getState($level, $type)
{
$nextStateLevel = $level-$this->maxLevel;
if ($level>$this->maxLevel) {
return [
'state' => $this->name,
'level' => '满级'
];
} else {
return [
'state' => $this->name,
'level' => $level
];
}
}
}
客户端:
<?php
include './State.php';
include './BronzeState.php';
include './DiamondState.php';
include './GoldState.php';
include './PlatinumState.php';
include './SilverState.php';
include './Hero.php';
include './OrangeHero.php';
include './PurpleHero.php';
class Client
{
public function main()
{
//我的橙色英雄
$level = 234;
$orangeHero = new OrangeHero();
echo '我的橙色英雄<br/>';
print_r($orangeHero->getState($level));
echo '<hr/>';
//我的紫色英雄
$level = 200;
$purpleHero = new PurpleHero();
echo '我的紫色英雄<br/>';
print_r($purpleHero->getState($level));
}
}
$client = new Client();
$client->main();
UML图
如果需要进行增加一个在青铜和白银之间加入一个段位,则扩展一个状态类,然后修改BronzeState(青铜状态类)即可。如果需要增加其他英雄,则直接扩展一个英雄类即可。
下一篇
初识设计模式——原型模式