您的位置:首页 > 编程语言 > Java开发

JAVA设计模式(20) —<行为型>状态模式(State)

2015-11-01 22:53 549 查看


1 定义:

状态模式(State)
Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.(当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类)。


1.1 通用类图:



一个更易理解的类图:



在状态模式结构图中包含如下几个角色:

环境类(Context):环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。

抽象状态类(State):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。

具体状态类(ConcreteState):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

在状态模式中,我们将对象在不同状态下的行为封装到不同的状态类中,为了让系统具有更好的灵活性和可扩展性,同时对各状态下的共有行为进行封装,我们需要对状态进行抽象,引入了抽象状态类角色


1.2 通用代码:

[java] view
plaincopy

public abstract class State {

// 定义一个环境角色,提供子类访问

protected Context context;

// 设置环境角色

public void setContext(Context _context) {

this.context = _context;

}

// 行为1

public abstract void handle1();

// 行为2

public abstract void handle2();

}

public class ConcreteState1 extends State {

@Override

public void handle1() {

// 本状态下必须处理的逻辑

}

@Override

public void handle2() {

// 设置当前状态为stat2

super.context.setCurrentState(Context.STATE2);

// 过渡到state2状态,由Context实现

super.context.handle2();

}

}

public class ConcreteState2 extends State {

@Override

public void handle1() {

// 设置当前状态为stat1

super.context.setCurrentState(Context.STATE1);

// 过渡到state1状态,由Context实现

super.context.handle1();

}

@Override

public void handle2() {

// 本状态下必须处理的逻辑

}

}

public class Context {

// 定义状态

public final static State STATE1 = new ConcreteState1();

public final static State STATE2 = new ConcreteState2();

// 当前状态

private State CurrentState;

// 获得当前状态

public State getCurrentState() {

return CurrentState;

}

// 设置当前状态

public void setCurrentState(State currentState) {

this.CurrentState = currentState;

// 切换状态

this.CurrentState.setContext(this);

}

// 行为委托

public void handle1() {

this.CurrentState.handle1();

}

public void handle2() {

this.CurrentState.handle2();

}

}

public class Client {

public static void main(String[] args) {

// 定义环境角色

Context context = new Context();

// 初始化状态

context.setCurrentState(new ConcreteState1());

// 行为执行

context.handle1();

context.handle2();

}

}


2 优点

结构清晰:避免了过多的switch……case或者if……else语句,避免了程序的复杂性,提高系统的可维护性。
遵循设计原则:很好的体现了开闭原则和单一职责原则,每个状态都是一个子类,你要增加状态就要增加子类,你要修改状态,只要修改一个子类就可以了。
封装性非常好:这也是状态模式的基本要求,状态变换放置到类的内部实现,外部的调用不知道内部如何实现和行为的变换。


3 缺点

1、状态模式的使用必然会增加系统类和对象的个数,子类会太多,即类膨胀。

2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。

3、状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。


4 应用场景

4.1 行为随状态改变而改变的场景:如权限设计(人员的状态不同,即使执行相同的行为,结果也不同);

4.2 条件、分支判断语句的替代者(有待思量)
工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等


5 注意事项

状态模式适用于当某个对象在它的状态发生改变时,它的行为也随着发生比较大的变化,也就是说行为受状态约束的情况下可以使用状态模式,而且使用时,对象的状态最好不要超过5个。


6 扩展


6.1对象的状态之间相互转换的两种方式:

(1) 统一由环境类来负责状态之间的转换,此时,环境类还充当了状态管理器(State Manager)角色,在环境类的业务方法中通过对某些属性值的判断实现状态转换,还可以提供一个专门的方法用于实现属性判断和状态转换,如下代码片段所示:

[java] view
plaincopy

……

public void changeState() {

//判断属性值,根据属性值进行状态转换

if (value == 0) {

this.setState(new ConcreteStateA());

}

else if (value == 1) {

this.setState(new ConcreteStateB());

}

......

}

……

(2) 由具体状态类来负责状态之间的转换,可以在具体状态类的业务方法中判断环境类的某些属性值再根据情况为环境类设置新的状态对象,实现状态转换,同样,也可以提供一个专门的方法来负责属性值的判断和状态转换。此时,状态类与环境类之间就将存在依赖或关联关系,因为状态类需要访问环境类中的属性值,如下代码片段所示:

[java] view
plaincopy

……

public void changeState(Context ctx) {

//根据环境对象中的属性值进行状态转换

if (ctx.getValue() == 1) {

ctx.setState(new ConcreteStateB());

}

else if (ctx.getValue() == 2) {

ctx.setState(new ConcreteStateC());

}

......

}

……

6.2使用环境类实现状态转换

在状态模式中实现状态转换时,具体状态类可通过调用环境类Context的setState()方法进行状态的转换操作,也可以统一由环境类Context来实现状态的转换。此时,增加新的具体状态类可能需要修改其他具体状态类或者环境类的源代码,否则系统无法转换到新增状态。但是对于客户端来说,无须关心状态类,可以为环境类设置默认的状态类,而将状态的转换工作交给具体状态类或环境类来完成,具体的转换细节对于客户端而言是透明的。

在上面的“银行账户状态转换”实例中,我们通过具体状态类来实现状态的转换,在每一个具体状态类中都包含一个stateCheck()方法,在该方法内部实现状态的转换,除此之外,我们还可以通过环境类来实现状态转换,环境类作为一个状态管理器,统一实现各种状态之间的转换操作

下面通过一个包含循环状态的简单实例来说明如何使用环境类实现状态转换:
Sunny软件公司某开发人员欲开发一个屏幕放大镜工具,其具体功能描述如下:

用户单击“放大镜”按钮之后屏幕将放大一倍,再点击一次“放大镜”按钮屏幕再放大一倍,第三次点击该按钮后屏幕将还原到默认大小。
可以考虑使用状态模式来设计该屏幕放大镜工具,我们定义三个屏幕状态类NormalState、LargerState和LargestState来对应屏幕的三种状态,分别是正常状态、二倍放大状态和四倍放大状态,屏幕类Screen充当环境类,其结构如图6所示:



图6 屏幕放大镜工具结构图

本实例核心代码如下所示:

[java] view
plaincopy

//屏幕类

class Screen {

//枚举所有的状态,currentState表示当前状态

private State currentState, normalState, largerState, largestState;

public Screen() {

this.normalState = new NormalState(); //创建正常状态对象

this.largerState = new LargerState(); //创建二倍放大状态对象

this.largestState = new LargestState(); //创建四倍放大状态对象

this.currentState = normalState; //设置初始状态

this.currentState.display();

}

public void setState(State state) {

this.currentState = state;

}

//单击事件处理方法,封转了对状态类中业务方法的调用和状态的转换

public void onClick() {

if (this.currentState == normalState) {

this.setState(largerState);

this.currentState.display();

}

else if (this.currentState == largerState) {

this.setState(largestState);

this.currentState.display();

}

else if (this.currentState == largestState) {

this.setState(normalState);

this.currentState.display();

}

}

}

//抽象状态类

abstract class State {

public abstract void display();

}

//正常状态类

class NormalState extends State{

public void display() {

System.out.println("正常大小!");

}

}

//二倍状态类

class LargerState extends State{

public void display() {

System.out.println("二倍大小!");

}

}

//四倍状态类

class LargestState extends State{

public void display() {

System.out.println("四倍大小!");

}

}

在上述代码中,所有的状态转换操作都由环境类Screen来实现,此时,环境类充当了状态管理器角色。如果需要增加新的状态,例如“八倍状态类”,需要修改环境类,这在一定程度上违背了“开闭原则”,但对其他状态类没有任何影响。

编写如下客户端代码进行测试:

[java] view
plaincopy

class Client {

public static void main(String args[]) {

Screen screen = new Screen();

screen.onClick();

screen.onClick();

screen.onClick();

}

}

输出结果如下:

正常大小!

二倍大小!

四倍大小!

正常大小!

6.3共享状态

在有些情况下,多个环境对象可能需要共享同一个状态,如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员对象

下面通过一个简单实例来说明如何实现共享状态:

如果某系统要求两个开关对象要么都处于开的状态,要么都处于关的状态,在使用时它们的状态必须保持一致,开关可以由开转换到关,也可以由关转换到开。
可以使用状态模式来实现开关的设计,其结构如图5所示:



图5 开关及其状态设计结构图

开关类Switch代码如下所示:

[java] view
plaincopy

class Switch {

private static State state,onState,offState; //定义三个静态的状态对象

private String name;

public Switch(String name) {

this.name = name;

onState = new OnState();

offState = new OffState();

this.state = onState;

}

public void setState(State state) {

this.state = state;

}

public static State getState(String type) {

if (type.equalsIgnoreCase("on")) {

return onState;

}

else {

return offState;

}

}

//打开开关

public void on() {

System.out.print(name);

state.on(this);

}

//关闭开关

public void off() {

System.out.print(name);

state.off(this);

}

}

抽象状态类如下代码所示:

[java] view
plaincopy

abstract class State {

public abstract void on(Switch s);

public abstract void off(Switch s);

}

两个具体状态类如下代码所示:

[java] view
plaincopy

//打开状态

class OnState extends State {

public void on(Switch s) {

System.out.println("已经打开!");

}

public void off(Switch s) {

System.out.println("关闭!");

s.setState(Switch.getState("off"));

}

}

//关闭状态

class OffState extends State {

public void on(Switch s) {

System.out.println("打开!");

s.setState(Switch.getState("on"));

}

public void off(Switch s) {

System.out.println("已经关闭!");

}

}

编写如下客户端代码进行测试:

[java] view
plaincopy

class Client {

public static void main(String args[]) {

Switch s1,s2;

s1=new Switch("开关1");

s2=new Switch("开关2");

s1.on();

s2.on();

s1.off();

s2.off();

s2.on();

s1.on();

}

}

输出结果如下:

开关1已经打开!

开关2已经打开!

开关1关闭!

开关2已经关闭!

开关2打开!

开关1已经打开!
从输出结果可以得知两个开关共享相同的状态,如果第一个开关关闭,则第二个开关也将关闭,再次关闭时将输出“已经关闭”;打开时也将得到类似结果。


7 范例


7.1酒店房间预订

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



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

[java] view
plaincopyprint?

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啦!所以这里我们就通过动作将所有的状态全面整合起来。分析得这里有四个动作:预订、入住、退订、退房。如下:

[java] view
plaincopyprint?

/**

* @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图如下:



首先是状态接口:State

[html] view
plaincopyprint?

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个状态类,这个三个状态分别对于这:空闲、预订、入住。其中空闲可以完成预订和入住两个动作,预订可以完成入住和退订两个动作,入住可以退房。

[java] view
plaincopyprint?

/**

* @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() {

//不需要做操作

}

}

[java] view
plaincopyprint?

/**

* @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()); //变成空闲状态

}

}

[java] view
plaincopyprint?

/**

* @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() {

//不需要做操作

}

}

最后是测试类

[java] view
plaincopyprint?

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]);

}

}

运行结果



7.2银行系统中的账户设计

Sunny软件公司欲为某银行开发一套信用卡业务系统,银行账户(Account)是该系统的核心类之一,通过分析,Sunny软件公司开发人员发现在该系统中,账户存在三种状态,且在不同状态下账户存在不同的行为,具体说明如下:

(1) 如果账户中余额大于等于0,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款;

(2) 如果账户中余额小于0,并且大于-2000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;

(3) 如果账户中余额等于-2000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,同时也将按天计算利息;

(4) 根据余额的不同,以上三种状态可发生相互转换。
Sunny软件公司开发人员对银行账户类进行分析,绘制了如图2所示UML状态图:



图2 银行账户状态图

在图2中,NormalState表示正常状态,OverdraftState表示透支状态,RestrictedState表示受限状态,在这三种状态下账户对象拥有不同的行为,方法deposit()用于存款,withdraw()用于取款,computeInterest()用于计算利息,stateCheck()用于在每一次执行存款和取款操作后根据余额来判断是否要进行状态转换并实现状态转换,相同的方法在不同的状态中可能会有不同的实现。为了实现不同状态下对象的各种行为以及对象状态之间的相互转换,Sunny软件公司开发人员设计了一个较为庞大的账户类Account,其中部分代码如下所示:

[java] view
plaincopy

class Account {

private String state; //状态

private int balance; //余额

......

//存款操作

public void deposit() {

//存款

stateCheck();

}

//取款操作

public void withdraw() {

if (state.equalsIgnoreCase("NormalState") || state.equalsIgnoreCase("OverdraftState ")) {

//取款

stateCheck();

}

else {

//取款受限

}

}

//计算利息操作

public void computeInterest() {

if(state.equalsIgnoreCase("OverdraftState") || state.equalsIgnoreCase("RestrictedState ")) {

//计算利息

}

}

//状态检查和转换操作

public void stateCheck() {

if (balance >= 0) {

state = "NormalState";

}

else if (balance > -2000 && balance < 0) {

state = "OverdraftState";

}

else if (balance == -2000) {

state = "RestrictedState";

}

else if (balance < -2000) {

//操作受限

}

}

......

}

分析上述代码,我们不难发现存在如下几个问题:

(1) 几乎每个方法中都包含状态判断语句,以判断在该状态下是否具有该方法以及在特定状态下该方法如何实现,导致代码非常冗长,可维护性较差;

(2) 拥有一个较为复杂的stateCheck()方法,包含大量的if…else if…else…语句用于进行状态转换,代码测试难度较大,且不易于维护;

(3) 系统扩展性较差,如果需要增加一种新的状态,如冻结状态(Frozen State,在该状态下既不允许存款也不允许取款),需要对原有代码进行大量修改,扩展起来非常麻烦。

为了解决这些问题,我们可以使用状态模式,在状态模式中,我们将对象在每一个状态下的行为和状态转移语句封装在一个个状态类中,通过这些状态类来分散冗长的条件转移语句,让系统具有更好的灵活性和可扩展性,状态模式可以在一定程度上解决上述问题

完整解决方案

Sunny软件公司开发人员使用状态模式来解决账户状态的转换问题,客户端只需要执行简单的存款和取款操作,系统根据余额将自动转换到相应的状态,其基本结构如图4所示:



图4 银行账户结构图

在图4中,Account充当环境类角色,AccountState充当抽象状态角色,NormalState、OverdraftState和RestrictedState充当具体状态角色。完整代码如下所示:

温馨提示:代码有点长,需要有耐心!



[java] view
plaincopy

//银行账户:环境类

class Account {

private AccountState state; //维持一个对抽象状态对象的引用

private String owner; //开户名

private double balance = 0; //账户余额

public Account(String owner,double init) {

this.owner = owner;

this.balance = balance;

this.state = new NormalState(this); //设置初始状态

System.out.println(this.owner + "开户,初始金额为" + init);

System.out.println("---------------------------------------------");

}

public double getBalance() {

return this.balance;

}

public void setBalance(double balance) {

this.balance = balance;

}

public void setState(AccountState state) {

this.state = state;

}

public void deposit(double amount) {

System.out.println(this.owner + "存款" + amount);

state.deposit(amount); //调用状态对象的deposit()方法

System.out.println("现在余额为"+ this.balance);

System.out.println("现在帐户状态为"+ this.state.getClass().getName());

System.out.println("---------------------------------------------");

}

public void withdraw(double amount) {

System.out.println(this.owner + "取款" + amount);

state.withdraw(amount); //调用状态对象的withdraw()方法

System.out.println("现在余额为"+ this.balance);

System.out.println("现在帐户状态为"+ this. state.getClass().getName());

System.out.println("---------------------------------------------");

}

public void computeInterest()

{

state.computeInterest(); //调用状态对象的computeInterest()方法

}

}

//抽象状态类

abstract class AccountState {

protected Account acc;

public abstract void deposit(double amount);

public abstract void withdraw(double amount);

public abstract void computeInterest();

public abstract void stateCheck();

}

//正常状态:具体状态类

class NormalState extends AccountState {

public NormalState(Account acc) {

this.acc = acc;

}

public NormalState(AccountState state) {

this.acc = state.acc;

}

public void deposit(double amount) {

acc.setBalance(acc.getBalance() + amount);

stateCheck();

}

public void withdraw(double amount) {

acc.setBalance(acc.getBalance() - amount);

stateCheck();

}

public void computeInterest()

{

System.out.println("正常状态,无须支付利息!");

}

//状态转换

public void stateCheck() {

if (acc.getBalance() > -2000 && acc.getBalance() <= 0) {

acc.setState(new OverdraftState(this));

}

else if (acc.getBalance() == -2000) {

acc.setState(new RestrictedState(this));

}

else if (acc.getBalance() < -2000) {

System.out.println("操作受限!");

}

}

}

//透支状态:具体状态类

class OverdraftState extends AccountState

{

public OverdraftState(AccountState state) {

this.acc = state.acc;

}

public void deposit(double amount) {

acc.setBalance(acc.getBalance() + amount);

stateCheck();

}

public void withdraw(double amount) {

acc.setBalance(acc.getBalance() - amount);

stateCheck();

}

public void computeInterest() {

System.out.println("计算利息!");

}

//状态转换

public void stateCheck() {

if (acc.getBalance() > 0) {

acc.setState(new NormalState(this));

}

else if (acc.getBalance() == -2000) {

acc.setState(new RestrictedState(this));

}

else if (acc.getBalance() < -2000) {

System.out.println("操作受限!");

}

}

}

//受限状态:具体状态类

class RestrictedState extends AccountState {

public RestrictedState(AccountState state) {

this.acc = state.acc;

}

public void deposit(double amount) {

acc.setBalance(acc.getBalance() + amount);

stateCheck();

}

public void withdraw(double amount) {

System.out.println("帐号受限,取款失败");

}

public void computeInterest() {

System.out.println("计算利息!");

}

//状态转换

public void stateCheck() {

if(acc.getBalance() > 0) {

acc.setState(new NormalState(this));

}

else if(acc.getBalance() > -2000) {

acc.setState(new OverdraftState(this));

}

}

}

编写如下客户端测试代码:

[java] view
plaincopy

class Client {

public static void main(String args[]) {

Account acc = new Account("段誉",0.0);

acc.deposit(1000);

acc.withdraw(2000);

acc.deposit(3000);

acc.withdraw(4000);

acc.withdraw(1000);

acc.computeInterest();

}

}

编译并运行程序,输出结果如下:

段誉开户,初始金额为0.0

---------------------------------------------

段誉存款1000.0

现在余额为1000.0

现在帐户状态为NormalState

---------------------------------------------

段誉取款2000.0

现在余额为-1000.0

现在帐户状态为OverdraftState

---------------------------------------------

段誉存款3000.0

现在余额为2000.0

现在帐户状态为NormalState

---------------------------------------------

段誉取款4000.0

现在余额为-2000.0

现在帐户状态为RestrictedState

---------------------------------------------

段誉取款1000.0

帐号受限,取款失败

现在余额为-2000.0

现在帐户状态为RestrictedState

---------------------------------------------

计算利息!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: