在介绍状态模式之前,我们先来看这样一个实例:你公司力排万难终于获得某个酒店的系统开发项目,并且最终落到了你的头上。下图是他们系统的主要工作(够简单)。

当你第一眼看到这个系统的时候你就看出来了这是一个状态图,每个框框都代表了房间的状态,箭头表示房间状态的转换。分析如下:房间有三个状态:空闲、已预订、已入住,状态与状态之间可以根据客户的动作来进行转换。定义每个状态的值。

public static final int FREEMTIME_STATE = 0;  //空闲状态
public static final int BOOKED_STATE = 1; //已预订状态
public static final int CHECKIN_STATE = 2; //入住状态 int state = FREEMTIME_STATE; //初始状态

通过客户的动作将每个状态整合起来,对于这个“最简单”的方式肯定是if…else if…else啦!所以这里我们就通过动作将所有的状态全面整合起来。分析得这里有四个动作:预订、入住、退订、退房。如下:

/**
* @desc 预订
* @return void
*/
public void bookRoom(){
if(state == FREEMTIME_STATE){ //空闲可预订
if(count > 0){
System.out.println("空闲房间,完成预订...");
state = BOOKED_STATE; //改变状态:已预订
count --;
//房间预订完了,提示客户没有房源了
if(count == 0){
System.out.println("不好意思,房间已经预订完,欢迎您下次光临...");
}
}
else{
System.out.println("不好意思,已经没有房间了....");
}
}
else if(state == BOOKED_STATE){
System.out.println("该房间已经被预订了...");
}
else if(state == CHECKIN_STATE){
System.out.println("该房间已经有人入住了...");
}
} /**
* @desc 入住
* @return void
*/
public void checkInRoom(){
if(state == FREEMTIME_STATE){
if(count > 0){
System.out.println("空闲房间,入住...");
state = CHECKIN_STATE; //改变状态:已预订
count --;
//房间预订完了,提示客户没有房源了
if(count == 0){
System.out.println("不好意思,房间已经预订完,欢迎您下次光临...");
}
}
else{
System.out.println("不好意思,已经没有房间了....");
} }
else if(state == BOOKED_STATE){
if("如果该房间是您预订的"){
System.out.println("入住....");
state = CHECKIN_STATE;
}
else{
System.out.println("您没有预订该房间,请先预订...");
}
}
else if(state == CHECKIN_STATE){
System.out.println("该房间已经入住了...");
}
} /**
* @desc 退订
* @return void
*/
public void unsubscribeRoom(){
if(state == FREEMTIME_STATE){
}
else if(state == CHECKIN_STATE){ }
else if(state == BOOKED_STATE){
System.out.println("已退订房间...");
state = FREEMTIME_STATE;
count ++;
}
} /**
* @desc 退房
* @return void
*/
public void checkOutRoom(){
if(state == FREEMTIME_STATE){ }
else if(state == BOOKED_STATE){ }
else if(state == CHECKIN_STATE){
System.out.println("已退房..");
state = FREEMTIME_STATE;
count++;
}
}

对于上面的代码你是否满意呢?满意那么你就没有必要往下看了,不满意我们接着讲。

正当你完成这个“复杂”if..else if …else时(我都写了一会儿),你客户说,我们需要将某些房间保留下来以作为备用(standbyState),于是你发现你悲剧了,因为你发现你要在所有的操作里都要判断该房间是否为备用房间。当你老大经过你身边的时候发现你正在纠结怎么改的时候,你老大就问你为什么不换一个角度思考以状态为原子来改变它的行为,而不是通过行为来改变状态呢?于是你就学到了状态模式。

一、模式定义

在很多情况下,一个对象的行为取决于它的一个或多个变化的属性,这些属性我们称之为状态,这个对象称之为状态对象。对于状态对象而已,它的行为依赖于它的状态,比如你要预订房间,那么只有当该房间为空闲时你才能预订,你想入住该房间也只有当你预订了该房间或者该房间为空闲时。对于这样的一个对象,当它在于外部事件产生互动的时候,其内部状态就会发生改变,从而使得他的行为也随之发生改变。

那么何为状态模式呢?所谓状态模式就是允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。

二、模式结构

下图为状态模式的UML图。

状态模式包含如下角色: 
         Context: 环境类。可以包括一些内部状态。 
         State: 抽象状态类。State定义了一个所有具体状态的共同接口,任何状态都实现这个相同的接口,这样一来,状态之间就可以互相转换了。 
         ConcreteState: 具体状态类。具体状态类,用于处理来自Context的请求,每一个ConcreteState都提供了它对自己请求的实现,所以,当Context改变状态时行为也会跟着改变。

三、模式实现

依然是上面那个酒店的实例。对于该实例的UML图如下:

首先是状态接口:State

public interface State {
/**
* @desc 预订房间
* @return void
*/
public void bookRoom(); /**
* @desc 退订房间
* @return void
*/
public void unsubscribeRoom(); /**
* @desc 入住
* @return void
*/
public void checkInRoom(); /**
* @desc 退房
* @return void
*/
public void checkOutRoom(); }

然后是房间类

public class Room {
/*
* 房间的三个状态
*/
State freeTimeState; //空闲状态
State checkInState; //入住状态
State bookedState; //预订状态 State state ; public Room(){
freeTimeState = new FreeTimeState(this);
checkInState = new CheckInState(this);
bookedState = new BookedState(this); state = freeTimeState ; //初始状态为空闲
} /**
* @desc 预订房间
* @return void
*/
public void bookRoom(){
state.bookRoom();
} /**
* @desc 退订房间
* @return void
*/
public void unsubscribeRoom(){
state.unsubscribeRoom();
} /**
* @desc 入住
* @return void
*/
public void checkInRoom(){
state.checkInRoom();
} /**
* @desc 退房
* @return void
*/
public void checkOutRoom(){
state.checkOutRoom();
} public String toString(){
return "该房间的状态是:"+getState().getClass().getName();
} /*
* getter和setter方法
*/ public State getFreeTimeState() {
return freeTimeState;
} public void setFreeTimeState(State freeTimeState) {
this.freeTimeState = freeTimeState;
} public State getCheckInState() {
return checkInState;
} public void setCheckInState(State checkInState) {
this.checkInState = checkInState;
} public State getBookedState() {
return bookedState;
} public void setBookedState(State bookedState) {
this.bookedState = bookedState;
} public State getState() {
return state;
} public void setState(State state) {
this.state = state;
} }

然后是3个状态类,这个三个状态分别对于这:空闲、预订、入住。其中空闲可以完成预订和入住两个动作,预订可以完成入住和退订两个动作,入住可以退房。

/**
* @project: design_state
* @author chenssy
* @date 2013-8-24
* @Description: 空闲状态只能预订和入住
*/
public class FreeTimeState implements State { Room hotelManagement; public FreeTimeState(Room hotelManagement){
this.hotelManagement = hotelManagement;
} public void bookRoom() {
System.out.println("您已经成功预订了...");
  .setState(hotelManagement.getBookedState()); //状态变成已经预订
} public void checkInRoom() {
System.out.println("您已经成功入住了...");
hotelManagement.setState(hotelManagement.getCheckInState()); //状态变成已经入住
} public void checkOutRoom() {
//不需要做操作
} public void unsubscribeRoom() {
//不需要做操作
} }
/**
* @project: design_state
* @author chenssy
* @date 2013-8-24
* @Description: 入住状态房间只能退房
*/
public class BookedState implements State {
Room hotelManagement; public BookedState(Room hotelManagement) {
this.hotelManagement = hotelManagement;
} public void bookRoom() {
System.out.println("该房间已近给预定了...");
} public void checkInRoom() {
System.out.println("入住成功...");
hotelManagement.setState(hotelManagement.getCheckInState()); //状态变成入住
} public void checkOutRoom() {
//不需要做操作
} public void unsubscribeRoom() {
System.out.println("退订成功,欢迎下次光临...");
hotelManagement.setState(hotelManagement.getFreeTimeState()); //变成空闲状态
} }
/**
* @project: design_state
* @author chenssy
* @date 2013-8-24
* @Description: 入住可以退房
*/
public class CheckInState implements State {
Room hotelManagement;
public CheckInState(Room hotelManagement) {
this.hotelManagement = hotelManagement;
} public void bookRoom() {
System.out.println("该房间已经入住了...");
} public void checkInRoom() {
System.out.println("该房间已经入住了...");
} public void checkOutRoom() {
System.out.println("退房成功....");
hotelManagement.setState(hotelManagement.getFreeTimeState()); //状态变成空闲
} public void unsubscribeRoom() {
//不需要做操作
} }

最后是测试类

public class Test {
public static void main(String[] args) {
//有3间房
Room[] rooms = new Room[2];
//初始化
for(int i = 0 ; i < rooms.length ; i++){
rooms[i] = new Room();
}
//第一间房
rooms[0].bookRoom(); //预订
rooms[0].checkInRoom(); //入住
rooms[0].bookRoom(); //预订
System.out.println(rooms[0]);
System.out.println("---------------------------"); //第二间房
rooms[1].checkInRoom();
rooms[1].bookRoom();
rooms[1].checkOutRoom();
rooms[1].bookRoom();
System.out.println(rooms[1]);
} }

运行结果

四、模式优缺点

优点

1、封装了转换规则。 
      2、枚举可能的状态,在枚举状态之前需要确定状态种类。 
      3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 
      4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 
      5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

缺点

1、状态模式的使用必然会增加系统类和对象的个数。 
      2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 
      3、状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

五、模式适用场景

1、对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。 
     2、代码中包含大量与对象状态有关的条件语句

六、模式总结

     1、状态模式允许一个对象基于内部状态而拥有不同的行为。

     2、Context会将行为委托给当前状态对象。

     3、状态模式对“开闭原则”支持不是很好。


设计模式:状态模式(Status)的更多相关文章

  1. 14. 星际争霸之php设计模式--状态模式

    题记==============================================================================本php设计模式专辑来源于博客(jymo ...

  2. [Head First设计模式]生活中学设计模式——状态模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 [Head First设计模式]山西面馆中的设计模式— ...

  3. JAVA 设计模式 状态模式

    用途 状态模式 (State) 当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类. 状态模式是一种行为型模式. 结构

  4. 深入浅出设计模式——状态模式(State Pattern)

    模式动机 在很多情况下,一个对象的行为取决于一个或多个动态变化的属性,这样的属性叫做状态,这样的对象叫做有状态的 (stateful)对象,这样的对象状态是从事先定义好的一系列值中取出的.当一个这样的 ...

  5. C++设计模式——状态模式

    前言 在实际开发中,我们经常会遇到这种情况:一个对象有多种状态,在每一个状态下,都会有不同的行为.那么在代码中我们经常是这样实现的. typedef enum tagState { state, st ...

  6. C#设计模式--状态模式

    设计模式: 状态模式(State Pattern) 简单介绍: 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式. 在状态模式中,我们创建表示 ...

  7. Java设计模式—状态模式

    状态模式又是一个比较难的设计模式 定义如下: 当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类. 个人理解:通俗的讲,状态模式就是状态的改变引起了行为的改变,但是,我们只能看到行为的 ...

  8. 设计模式-状态模式(State Pattern)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 状态模式简介 状态模式允许一个对象在其内部状态改变的时候改变它的行为,他的内部会存着好几种状态, ...

  9. 设计模式-状态模式(State)

    状态模式是行为模式的一种,状态模式允许改变对象内部状态来改变对象的行为. 角色和职责: 1.上下文(Context)-Order:    拥有内部的状态 2.状态接口(Status)-State: 一 ...

随机推荐

  1. Comet OJ - Contest #3 D 可爱的菜菜子 线段树+线性基

    题意 给你一个长度为 \(n\) 的整数序列 \(a_1, a_2, \ldots, a_n\),你需要实现以下两种操作,每个操作都可以用四个整数 \(opt\ l\ r\ v\) 来表示: \(op ...

  2. axios 的坑

    必须安装axios 和qs 1.main.js中的配置 import axios from 'axios' import qs from 'qs'; axios.defaults.headers.po ...

  3. windows 安装多个版本的jdk后修改 环境变量不起作用

    本机已经安装了jdk1.6,而比较早期的项目需要依赖jdk1.5,于是同时在本机安装了jdk1.5和jdk1.6. 安装jdk1.5前,执行java -version得到java version &q ...

  4. Android学习_内容提示器

    内容提供器 1. 创建自己的内容提供器 1)         继承ContentProvider类,重写6个方法:onCreate().query().insert().update().delete ...

  5. linux线程池thrmgr源码解析

    linux线程池thrmgr源码解析 1         thrmgr线程池的作用 thrmgr线程池的作用是提高程序的并发处理能力,在多CPU的服务器上运行程序,可以并发执行多个任务. 2      ...

  6. BigDecimal的保留位数和四舍五入的方法

    一 BigDecimal num = new BigDecimal("2.225667");//一般都会这样写最好 int count = num.scale(); System. ...

  7. MySQL:如何选取Table中的50到100行

    MySQL:如何选取Table中的50到100行 使用查询语句的时候,经常要返回前几条或者中间某几行数据,这个时候怎么办呢?不用担心,MySql已 经为我们提供了这样一个功能. ? 1 2 [sql] ...

  8. Requests 代理池

    Requests 本身不提供代理池,然而爬数据又要用,所以只能自己搞.其实还挺简单的.我也不知道为什么这么有用的 feature 一直没有被加入. import requests class Clie ...

  9. TCP发送窗口更新tcp_ack_update_window

    在tcp_ack接收ACK处理函数中,如果确认当前走慢速路径,那么会调用tcp_ack_update_window函数检查窗口是否需要更新并更新之,并且更新未确认数据的位置,即更新窗口左边沿: sta ...

  10. beta 2/2 阶段中间产物提交入口

    此作业要求参见https://edu.cnblogs.com/campus/nenu/2019fall/homework/9961 组名:胜利点 组长:贺敬文 队员:位军营,徐丽君,彭思雨,王志文 g ...