设计模式之状态模式
状态模式简介
有时候,会遇到这样的情形,某一个对象,有多种不同的状态,在不同的状态下,可能有不同的行为。要实现这样一种服务,可以使用最简单的if.else结构,但是呢,这种方式在状态比较多的时候就会使得代码变得比较冗长,每次有新的状态的时候,都需要修改该处代码,而且,在后期进行维护的时候,也不便于理解;还有另一一种方式,就是状态模式,所谓的状态模式,通俗地来讲,就是把对象的所有状态进行封装,将每一个状态独立抽取出来,形成一个状态类,这样,当需要增加新的状态的时候,就不需要更改原有的代码了。
状态模式的具体实现
这里来简单模拟一个号码卡的三种状态(正常状态,欠费状态,停机状态)变化来更加深入的了解状态模式。
当余额大于0的时候处于正常状态,当余额小于0但是大于-50的时候,处于欠费状态,此时依旧可以打电话,当欠费超过50的时候,处于停止状态,此时打电话功能将被限制
首先是没有使用状态模式的情景,也就是通过冗长的if.else语句结构来实现这样一个过程
/**
* Created by Huanfeng.Xu on 2017-06-26.
*/
public class Original {
public Original(double money){
this.money = money;
state = NORMAL;
}
private double money;
private String state;
private final static String STOP = "stop";
private final static String NORMAL = "normal";
private final static String OWNED = "owned";
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
// 调用具体的业务方法的时候任然需要进行状态的校验,根据不同的状态采取不同的行为
public void call(){
check();
if (STOP.equals(state)){
System.out.println("当前账号处于停机状态,不允许打电话也不允许接听电话");
}else{
if (OWNED.equals(state)){
System.out.println("当前账号处于欠费状态,请注意, 欠费金额 " + money);
}else if (NORMAL.equals(state)){
System.out.println("打电话中...");
}
money -= 10;
}
}
// 查看当前系统的状态
public void check(){
if (money < -50){
state = STOP;
}else if (money < 0 && money >= -50){
state = OWNED;
}else if (money >= 0){
state = NORMAL;
}
}
public void charge(double money){
this.money += money;
System.out.println("充值费用 " + money);
check();
}
}
从上面的代码中可以看到,每次执行一个业务方法的时候,都需要检查此时系统所处的状态,然后再根据不同的业务方法,判断不同状态下应该有的行为,当业务方法比较多的时候,整个的代码就会变得相对冗余了,而且修改起来几乎每一个业务方法都需要进行修改
接下来来看下使用状态模式之后的情况
/**
* Created by Huanfeng.Xu on 2017-06-26.
*/
public class Account {
private double money;
private State state;
// 省略set/get方法
public void call(){
state.call();
}
public void charge(double money){
state.charge(money);
}
}
// 抽象的状态类
abstract class State{
protected Account account;
public abstract void checkState();
public void charge(double money){
account.setMoney(account.getMoney() + money);
System.out.println("充值费用 " + money);
checkState();
}
public abstract void call();
}
//正常状态类
class Normal extends State{
public Normal(Account account){
this.account = account;
}
@Override
public void checkState() {
double currentMoney = account.getMoney();
if (currentMoney < 0 && currentMoney >= -50 ){
System.out.println("当前账号余额不足,已经处于欠费状态,欠费 " + account.getMoney());
account.setState(new Owned(account));
}else if (currentMoney < -50){
System.out.println("欠费过多,账号已经停机");
account.setState(new Stopped(account));
}
}
@Override
public void call() {
account.setMoney(account.getMoney() - 10);
System.out.println("打电话中...");
checkState();
}
}
//欠费状态类
class Owned extends State{
public Owned(Account account){
this.account = account;
}
@Override
public void checkState() {
double currentMoney = account.getMoney();
if (currentMoney > 0){
account.setState(new Normal(account));
}else if (currentMoney < -50){
account.setState(new Stopped(account));
}
}
@Override
public void call() {
System.out.println("当前账号处于欠费状态,请注意, 欠费金额 " + account.getMoney());
account.setMoney(account.getMoney() - 10);
checkState();
}
}
// 停机状态类
class Stopped extends State{
public Stopped(Account account){
this.account = account;
}
@Override
public void checkState() {
double currentMoney = account.getMoney();
if (currentMoney > 0){
System.out.println("账号已经恢复正常");
account.setState(new Normal(account));
}
}
@Override
public void call() {
System.out.println("当前账号处于停机状态,不允许打电话也不允许接听电话");
}
}
可以看到,此时将状态的变化交给了状态类本身,不用的状态类根据当前的行为,操作之后自动切换到不同的状态,此时就不再需要进行状态的检测了,账号类(环境类)所拥有的状态就是当前的状态了。并且把不同的状态的行为封装起来,此时,不同的状态类只需要负责自己的行为判断即可。当需要修改的时候,所需要修改的也仅仅是对应的状态类的行为了,而再是对每一个的业务方法都进行修改。
总结
状态模式,主要的思想就是把不同的状态及其行为抽取出来,封装成对应的状态对象,可以理解是将原本比较庞大的if.else语句抽取出来,分散到各自对应的状态之中,使得之后的修改都是直接针对对象进行修改,增加代码的可维护性。