热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

设计模式:状态模式(Status)

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

 

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

图片1

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


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图。

图片2

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


三、模式实现

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

aaaa

 

      首先是状态接口: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("您已经成功预订了...");
hotelManagement.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[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]);
}
}

      运行结果

1111

 

 

 

 

 

 

 

 


四、模式优缺点

优点

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


缺点

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


五、模式适用场景

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


六、模式总结


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

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

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

 





推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
  • Java值传递机制的说明及示例代码
    本文对Java值传递机制进行了详细说明,包括形参和实参的定义和传递方式,以及通过示例代码展示了交换值的方法。 ... [详细]
  • 本文介绍了Java中Hashtable的clear()方法,该方法用于清除和移除指定Hashtable中的所有键。通过示例程序演示了clear()方法的使用。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
author-avatar
小老虎颖儿
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有