某信用卡业务系统,银行账户存在3种状态,且在不同状态下存在不同的行为:
1)正常状态(余额大等于0),用户可以存款也可以取款;
2)透支状态(余额小于0且大于-2000),用户可以存款也可以取款,但需要对欠款支付利息。
3)受限状态(余额小等于-2000),用户只能存款,还需要对欠款支付利息。
图 伪代码实现上述需求
上面代码存在以下问题:
1)获取状态时,有好多个if分支,如果再增加几个状态,则需要增加判断条件,同时也不符合开闭原则。
2)在进行存取款操作时,有对状态进行判断的条件,行为受到状态的限制。
为了更好对具有多种状态的对象进行设计,可以使用一种被称作状态模式的设计模式。
1 状态模式
状态模式(State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的状态类。是一种对象行为型模式。
图 状态模式UML
Context:环境类,是拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。
State:抽象状态类,用于定义一个接口以封装与环境类的一个特定状态相关的行为。在抽象状态类中声明各种不同状态对应的方法,而在其子类中实现这些方法。
ConcreteState:具体状态类,是抽象状态类的子类,每个子类实现与环境类的一个状态相关的行为。
public class UserAccount {private double balance;private CardState cardState;public UserAccount(double balance) {this.balance = balance;cardState = new NormalCardState(this);}public double getBalance() {return balance;}public void setBalance(double balance) {this.balance = balance;}public void deposit(double money) {System.out.println("存钱 " + money);cardState.deposit(money);changeState();System.out.println("信用卡余额:"+ balance + ",状态是:" + cardState.getState());System.out.println("------------------------------");}public void withdraw(double money) {if (balance - money < 0) {System.out.println("借钱 " + money + ",利息利率是0.01");} else {System.out.println("取款 " + money);}cardState.withdraw(money);changeState();System.out.println("信用卡余额:"+ balance + ",状态是:" + cardState.getState());System.out.println("------------------------------");}public void changeState() {if (balance > 0) {if (!"正常".equals(cardState.getState())) cardState = new NormalCardState(this);} else if (balance > -2000) {if (!"透支".equals(cardState.getState())) cardState = new OverdraftCardState(this);} else {if (!"受限".equals(cardState.getState())) cardState = new LimitationCardState(this);}}public void setCardState(CardState cardState) {this.cardState = cardState;}
}public abstract class CardState {protected final UserAccount userAccount;public CardState(UserAccount userAccount) {this.userAccount = userAccount;}public abstract void deposit(double money); // 存款public abstract void withdraw(double money); // 取款public abstract void payInterest(); // 支付利息public abstract String getState(); // 获取状态}public class BankService {public static void main(String[] args) {// 开户UserAccount userAccount = new UserAccount(1000);userAccount.withdraw(500);userAccount.deposit(200);userAccount.withdraw(1000);userAccount.deposit(100);userAccount.withdraw(2000);userAccount.withdraw(500);}}//取款 500.0
//信用卡余额:500.0,状态是:正常
//------------------------------
//存钱 200.0
//信用卡余额:700.0,状态是:正常
//------------------------------
//借钱 1000.0,利息利率是0.01
//信用卡余额:-300.0,状态是:透支
//------------------------------
//存钱 100.0
//支付利息:-3.0
//信用卡余额:-203.0,状态是:透支
//------------------------------
//借钱 2000.0,利息利率是0.01
//支付利息:-2.0300000000000002
//信用卡余额:-2205.03,状态是:受限
//------------------------------
//借钱 500.0,利息利率是0.01
//该账户已受限,不能取款
//信用卡余额:-2205.03,状态是:受限
//------------------------------public class NormalCardState extends CardState{public NormalCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {userAccount.setBalance(userAccount.getBalance() - money);}@Overridepublic void payInterest() {}@Overridepublic String getState() {return "正常";}}public class OverdraftCardState extends CardState{public OverdraftCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() - money);}@Overridepublic void payInterest() {System.out.println("支付利息:" + userAccount.getBalance() * 0.01);userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));}@Overridepublic String getState() {return "透支";}
}public class LimitationCardState extends CardState{public LimitationCardState(UserAccount userAccount) {super(userAccount);}@Overridepublic void deposit(double money) {payInterest();userAccount.setBalance(userAccount.getBalance() + money);}@Overridepublic void withdraw(double money) {System.out.println("该账户已受限,不能取款");}@Overridepublic void payInterest() {System.out.println("支付利息:" + userAccount.getBalance() * 0.01);userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));}@Overridepublic String getState() {return "受限";}
}
使用状态模式后,在编码过程中,可以不要在关系具体状态,只需专注实现具体状态下的业务。
1.1 状态转换方式
在状态模式中,环境类的状态转换方式有两种:
1)在环境类完成转换。(上面代码是以这种形式)
2)在具体状态类中完成转换。
图 两种状态转换方式的比较
如果新增状态类,则两种方式都需要在各自的类中做修改。都不符合开闭原则。
1.2 共享状态
在有些情况下,多个环境类对象需要共享一个状态,这时需要把状态对象定义为一个静态成员对象。
需求:一个房间有两个开关来控制灯泡的开关。开关等功能是固定的(打开只能使灯泡亮起,关闭只能使灯泡熄灭。
public class LightSwitch {private final static LightState onState = new OnLightState(),offState = new OffLightState();private static LightState lightState = offState;private final String name;public LightSwitch(String name) {this.name = name;}public static void changeLightState(String type) {if ("on".equalsIgnoreCase(type)) {lightState = onState;} else {lightState = offState;}}public void off() {System.out.println(name + "关闭操作");lightState.off(this);}public void on() {System.out.println(name + "打开操作");lightState.on(this);}}public abstract class LightState {public abstract void on(LightSwitch lightSwitch);public abstract void off(LightSwitch lightSwitch);
}public class OnLightState extends LightState{@Overridepublic void on(LightSwitch lightSwitch) {System.out.println("灯泡已打开");System.out.println("--------------");}@Overridepublic void off(LightSwitch lightSwitch) {System.out.println("关闭成功");LightSwitch.changeLightState("off");System.out.println("--------------");}}public class OffLightState extends LightState{@Overridepublic void on(LightSwitch lightSwitch) {System.out.println("打开成功");LightSwitch.changeLightState("on");System.out.println("--------------");}@Overridepublic void off(LightSwitch lightSwitch) {System.out.println("灯泡已关闭");System.out.println("--------------");}}public class PeopleOpera {public static void main(String[] args) {LightSwitch lightSwitch1 = new LightSwitch("开关1");LightSwitch lightSwitch2 = new LightSwitch("开关2");lightSwitch1.on();lightSwitch2.off();lightSwitch1.off();lightSwitch1.on();lightSwitch2.on();lightSwitch2.off();}
}//开关1打开操作
//打开成功
//--------------
//开关2关闭操作
//关闭成功
//--------------
//开关1关闭操作
//灯泡已关闭
//--------------
//开关1打开操作
//打开成功
//--------------
//开关2打开操作
//灯泡已打开
//--------------
//开关2关闭操作
//关闭成功
//--------------
2 优缺点
优点:
1)环境类转换状态方式,封装状态的转换规则,对状态转换代码集中管理。
2)将所有与具体状态有关的行为都封装在一个类中。
3)可以让多个环境对象共享一个状态对象,从而减少系统中对象个数。
4)在具体状态类中转换状态方式,将状态转换逻辑与状态对象合成一起,避免使用庞大的条件语句块来将业务方法和状态转换代码交织在一起。
缺点:
1)增加了类和对象的个数。
2)实现较为复杂,增加了系统设计难度。
3)对开闭原则的支持并不好。
3 适用场景
1)对象的行为依赖它的状态,且状态之间互相转换。
2)代码中包含大量与对象状态有关的条件语句。