前言
最近在备考软件设计师,看了很多历年真题,无论是上午题还是下午题,都会至少有一题是设计模式的题目。下午题最后一道大题每一年基本固定是一道c++和java版的设计模式。正好趁这段时间好好加深一下设计模式的理解。
在做真题的时候,有一道关于航空公司会员的题目,这道题在一次历年真题中以面向对象大题出现,而另一年变成了最后一道设计模式大题,都是设计到状态模式。就索性从状态模式开始,一个一个吃透设计模式。
在了解状态模式之前,首先学习一下UML中的状态图。
UML状态图
状态图展现了一个状态机,它油状态、转换、事件、活动(动作)以及监护条件组成。
转换:两个状态之间的一种关系,表示对象将在源状态中执行一定的动作,并在某个特定事件发生而且某个特定的监护条件满足时进入目标状态;
动作:是一个可执行的原子操作,是不可中断的,其执行时间是可以忽略不计的。
比如,水壶开关关闭,想要烧水,首先触发开关打开这个事件,监护条件是满足水壶有水且水壶通电,如果都满足就执行烧水动作,水壶就转换成打开的状态。如下图所示:

状态模式
意图:允许一个对象内部结构发生变化时改变它的行为。对象看起来似乎修改了它的类。
状态模式结构

状态模式结构图
状态模式包含以下主要角色:
- Context定义客户感兴趣的接口(比如在main函数中调用);它的属性是一个State子类实例,定义当前状态。
- State定义一个接口以封装与Context的一个特定状态相关的行为。
- ConcreteStateA\B\C子类实现与Context的一个状态相关的行为。
适应性
- 一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
- 一个操作中含有庞大的多分支的条件语句(if-else语句),且这些分支依赖于该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每个条件分支放入一个独立的类中。这使得开发者可以根据对象的自身情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。通俗一点,即使用类来重构分支条件。
优点
- 状态将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足“单一职责”。
- 减少对象之间的依赖。每个状态都有一个独立对象维护起来。
- 有利于程序的扩展。如果出现新的条件分支,只需要定义新的具体状态子类,满足“开放-封闭原则 ”。
缺点
状态模式会增加系统的类和对象个数。
案例题
【例1】某个公司使用OKR完成情况进行打分决定员工某一个季度的绩效,绩效由优秀、良好、合格、背锅四种状态。
分数<0.8表示背锅
0.8<=分数<1.0表示合格
1.0<=分数<1.2表示良好
1.2<=分数表示优秀。
根据描述得到OKR的优秀到其他状态相互转换的状态图如下图所示。

在此其他状态在此就不一一画出来了。
现在抽象出一个抽象状态类(State)并定义了检查分数范围、计算分数等方法,然后为上图的每个状态设计一个具体的状态类,它们是优秀(SuperiorState)、良好(FineState)、合格(StandardState)、背锅(HeroState)4种状态。环境类MachineContext定义一个State实例,初始化为背锅(HeroState),并提供状态转换触发的方法。根据分析可以得到如下图所示的状态模式结构图。

代码如下:
void main() {
OKRContext ctx = new OKRContext();
ctx.addScore(0);
ctx.addScore(0.4);
ctx.addScore(0.5);
}
class OKRContext {
State state;
public OKRContext() {
this.state = new HeroState();
}
public SetState(State state) {
this.state = state;
}
public State GetState() {
return state;
}
public void addScore(float score) {
state.addScore(score, this);
}
}
abstract class State {
int score = 0;
String stateName ="";
public void addScore(float score, OKRContext ctx){
this.score = this.score + score;
checkScore(ctx);
system.out.println("当前状态:"+stateName);
}
public abstract void checkScore(OKRContext ctx);
}
class SuperiorState{
public SuperiorState() {
stateName = "优秀";
}
public void checkScore(OKRContext ctx){
if (score>=0.8 && score<1.0) {
ctx.SetState(new StandardState());
}
if (score>=1.0 && score < 1.2) {
ctx.SetState(new FineState());
}
if (score < 0.8) {
ctx.SetState(new HeroState());
}
}
}
class FineState{
public SuperiorState() {
stateName = "良好";
}
public void checkScore(OKRContext ctx){
if (score>=0.8 && score<1.0) {
ctx.SetState(new StandardState());
}
if (score >= 1.2) {
ctx.SetState(new SuperiorState());
}
if (score < 0.8) {
ctx.SetState(new HeroState());
}
}
}
class StandardState{
public SuperiorState() {
stateName = "合格";
}
public void checkScore(OKRContext ctx){
if (score>=1.0 && score < 1.2) {
ctx.SetState(new FineState());
}
if (score >= 1.2) {
ctx.SetState(new SuperiorState());
}
if (score < 0.8) {
ctx.SetState(new HeroState());
}
}
}
class HeroState{
public SuperiorState() {
stateName = "背锅侠";
}
public void checkScore(OKRContext ctx){
if (score>=1.0 && score < 1.2) {
ctx.SetState(new FineState());
}
if (score >= 1.2) {
ctx.SetState(new SuperiorState());
}
if (score>=0.8 && score<1.0) {
ctx.SetState(new StandardState());
}
}
}
【例2】某航空公司的会员积分系统将其会员划分为:普卡 (Basic) 、银卡(Silver)和金卡 (Gold)三个等级。非会员 (NonMember)可以申请成为普卡会员。会员的等级根据其一年内累积的里程数进行调整。描述会员等级调整的状态图如下图所示 。

现采用状态 (State) 模式实现上述场景,得到如下图所示的类图。
代码如下:
// 会员里程积分环境上下文
class FrequentFlyer {
State state;
int flymiles;
public void SetState(State state) {
this.state = state;
}
public State GetState(){
return state;
}
public FrequentFlyer() {
flymiles = 0;
state = new NoMember();
SetState(state);
}
public travel(int curmiles) {
flymiles = flymiles + state.travel(curmiles, this);
}
}
// 抽象状态类
// 特别注意:abstract定义的抽象类,其方法也要加上abstract关键字
abstract class State {
public int flymiles; // 当前公里数
public abstract int travel(int miles, FrequentFlyer ctx); // 里程计算方法
}
class NoMember extends State {
public int travel(int miles, FrequentFlyer ctx) {
return 0;
}
}
class Basic extends State {
public int travel(int miles, FrequentFlyer ctx) {
if (ctx.flymiles >= 25000 && ctx.flymiles < 50000) {
ctx.SetState(new Silver());
}
if (ctx.flymiles >= 50000) {
ctx.SetState(new Gold());
}
return miles;
}
}
class Silver extends State {
public int travel(int miles, FrequentFlyer ctx) {
if (ctx.flymiles < 25000) {
ctx.SetState(new Basic());
}
if (ctx.flymiles >= 50000) {
ctx.SetState(new Gold());
}
return miles + .25 * miles;
}
}
class Gold extends State {
public int travel(int miles, FrequentFlyer ctx) {
if (ctx.flymiles >= 25000 && ctx.flymiles < 50000) {
ctx.SetState(new Silver());
}
if (ctx.flymiles < 25000) {
ctx.SetState(new Basic());
}
return miles + .5 * miles;
}
}
717

被折叠的 条评论
为什么被折叠?



