> 文章列表 > Windows环境下实现设计模式——状态模式(JAVA版)

Windows环境下实现设计模式——状态模式(JAVA版)

Windows环境下实现设计模式——状态模式(JAVA版)

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现状态模式(设计模式)。

不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式,无从下手。为什么?因为你看的都是理论书籍。

我今天就在Windows操作系统上安装好JAVA的IDE编程工具,并用JAVA语言来实现一个状态模式,真实的实现一个,你看懂代码后,自然就明白了。

状态模式State Pattern(行为型设计模式)

定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

上面定义听懂了吗?莫名其妙看不懂对吧。所以我们还是来看看实现生活中的例子。

在日常生活中,人们总是有些各种喜怒哀乐的表情,而且这四种状态还会互相转化,包括人在内,很多事物都具有多种状态,而且在不同状态下会具有不同的行为,这些状态在特定条件下还将发生相互转换。而状态模式就是要模拟这种同一事物存在多种状态但又会变化转化的情况。状态模式是一种较为复杂的设计模式,它用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中的某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。

在软件系统中,有些对象具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。通常可以使用复杂的条件判断语句if else之类来进行状态判断和转换操作,但这样会导致代码的可维护性和灵活性下降。

状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式。状态模式将一个对象的状态从该对象中分离出来,封装到专门的状态类中,使得对象状态可以灵活变化,对于客户端而言,无须关心对象状态的转换以及对象所处的当前状态,无论对于何种状态的对象,客户端都可以一致处理。

在状态模式结构图中包含如下三个角色:Context(环境类)、State(抽象状态类)、ConcreteState(具体状态类)。环境类又称为上下文类,它是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

JAVA代码

public abstract class State {//声明抽象业务方法,不同的具体状态类可以不同的实现public abstract void handle();
}public class ConcreteState extends State {public void handle() {//方法具体实现代码}
}public class Context {private State state; //维持一个对抽象状态对象的引用private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化//设置状态对象public void setState(State state) {this.state = state;}public void request() {//其他代码state.handle(); //调用状态对象的业务方法//其他代码}
}

Context(环境类)实际上是真正拥有状态的对象,这个模式的主要精神就是将Context(环境类)中与状态有关的代码提取出来封装到专门的状态类中。在状态模式结构图中,在Context中定义了一个State对象。Context(环境类)与抽象状态类State之间存在单向关联关系,也可能存在依赖或者其他关联关系。

好,下面是关键的内容了,这部分是这个模式和其他设计模式一个很大的不同点,也是此模式复杂的地方,也就是在状态模式的使用过程中,一个对象的状态之间还可以进行相互转换,通常有两种实现状态转换的方式:

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

public void changeState() {//判断属性值,根据属性值进行状态转换if (value == 0) {this.setState(new ConcreteStateA());}else if (value == 1) {this.setState(new ConcreteStateB());}......
}

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

public void changeState(Context su) {//根据环境对象中的属性值进行状态转换if (su.getValue() == 1) {su.setState(new ConcreteStateB());}else if (su.getValue() == 2) {su.setState(new ConcreteStateC());}......
}

应用实例

某金融理财手机APP具有理财功能,每位用户在其中会开通个人账户(Account),APP用户账户存在三种状态,在不同状态下账户存在不同的行为,如果账户中余额大于等于0,则账户的状态为正常状态(Normal State),此时用户既可以向该账户存款也可以从该账户取款; 如果账户中余额小于0,并且大于-10000,则账户的状态为透支状态(Overdraft State),此时用户既可以向该账户存款也可以从该账户取款,但需要按天计算利息;如果账户中余额等于-10000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能再从中取款,同时也将按天计算利息。还有最后一种情况,如果取款导致金额小于-10000元了怎么办?我们这里要规定一下,如果小于这个数,马上宣布不正常状态不准用户操作。Check()用于在每一次执行存款和取款操作后根据余额来判断是否要进行状态转换并实现状态转换,相同的方法在不同的状态中可能会有不同的实现。为了实现不同状态下对象的各种行为以及对象状态之间的相互转化。

(1)Account:银行账户,环境类

package designpatterns.state;public class Account {private AccountState state;   //维持一个对抽象状态对象的引用private String owner;   //开户名private double balance = 0;   //账户余额public Account(String owner,double init) {this.owner = owner;this.balance = init;this.state = new NormalState(this); //设置初始状态System.out.println(this.owner + "开户,余额为" + this.balance);    }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());}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());        }public void Interest(){state.Interest(); //调用状态对象的Interest()方法}
}

(2)AccountState:账户状态类,抽象状态类

package designpatterns.state;public abstract class AccountState {protected Account cp;public abstract void deposit(double amount);public abstract void withdraw(double amount);public abstract void Interest();public abstract void Check();
}

(3)NormalState:正常状态类

package designpatterns.state;public class NormalState extends AccountState {public NormalState(Account cp) {this.cp = cp;}public NormalState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {cp.setBalance(cp.getBalance() - amount);Check();}public void Interest(){System.out.println("正常状态");}//状态转换public void Check() {if (cp.getBalance() > -10000 && cp.getBalance() <= 0) {cp.setState(new OverdraftState(this));}else if (cp.getBalance() == -10000) {cp.setState(new RestrictedState(this));}else if (cp.getBalance() < -10000) {System.out.println("不正常状态");}    }   
}  

(4)OverdraftState:透支状态类

package designpatterns.state;public class OverdraftState extends AccountState {public OverdraftState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {cp.setBalance(cp.getBalance() - amount);Check();}public void Interest(){System.out.println("利息数额");}//状态转换public void Check() {if (cp.getBalance() > 0) {cp.setState(new NormalState(this));}else if (cp.getBalance() == -10000) {cp.setState(new RestrictedState(this));}else if (cp.getBalance() < -10000) {System.out.println("不正常状态");}    }   
}  

(5)RestrictedState:透支状态类

package designpatterns.state;public class RestrictedState extends AccountState {public OverdraftState(AccountState state) {this.cp = state.cp;}public void deposit(double amount) {cp.setBalance(cp.getBalance() + amount);Check();}public void withdraw(double amount) {System.out.println("不能");}public void Interest(){System.out.println("利息数额");}//状态转换public void Check() {if (cp.getBalance() > 0) {cp.setState(new NormalState(this));}else if (cp.getBalance() > -10000) {cp.setState(new OverdraftState(this));}}   
}  

(6)Client:客户端测试类

package designpatterns.state;public class Client {public static void main(String args[]) {Account cp = new Account("小牛",0.0);cp.deposit(5000);cp.withdraw(10000);cp.deposit(6000);cp.withdraw(4000);cp.Interest();}
}

这篇设计模式文章是我写的最累的一篇了,写到这里已经很晚了,实在写不动了。输出结果大家自行推理就是了。无非就是随着用户又取钱又存钱的导致银行帐户出现各类状态转换问题。

状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。

各位小伙伴,这次我们就说到这里,下次我们再深入研究windows环境下的各类设计模式实现。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。