第22章:状态模式-处理对象的多种状态及其相互转换
定义:
状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
结构:
代码实现:
//抽象状态类
abstract class State {
//声明抽象业务方法,不同的具体状态类可以不同的实现
public abstract void handle();
}
//具体状态类
class ConcreteState extends State {
public void handle() {
//方法具体实现代码
}
}
//环境类:拥有多种状态的对象
class Context {
private State state; //维持一个对抽象状态对象的引用
private int value; //其他属性值,该属性值的变化可能会导致对象状态发生变化
//设置状态对象
public void setState(State state) {
this.state = state;
}
public void request() {
//其他代码
state.handle(); //调用状态对象的业务方法
//其他代码
}
}
- 可以在环境类中提供专门方法进行状态转换
public void changeState() {
//判断属性值,根据属性值进行状态转换
if (value == 0) {
this.setState(new ConcreteStateA());
} else if (value == 1) {
this.setState(new javaConcreteStateB());
}
//......
}
- 也可以在具体状态类中提供专门方法进行状态转换
public void changeState(Context ctx) {
//根据环境对象中的属性值进行状态转换
if (ctx.getValue() == 1) {
ctx.setState(new ConcreteStateB());
} else if (ctx.getValue() == 2) {
ctx.setState(new ConcreteStateC());
}
//......
}
应用实例:
银行信用卡系统
账户三种状态:
- 账户余额>=0,正常状态,可存可取
- -2000<账户余额<0,透支状态,可存可取,计算利息
- 账户余额=-2000,受限状态,只能存,计算利息
package wgg;
//账户类
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) {
//操作受限
}
}
//......
}
问题:
- 几乎每个方法都包含判断语句
- 复杂的stateCheck()方法,不易于维护
- 扩展需要修改大量源码
使用账户模式
//银行账户:环境类
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));
}
}
}
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();
}
}
多个环境共享同一个状态
两个开关同开同关,保持一致
//环境类开关
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);
}
}
//抽象状态
abstract class State {
public abstract void on(Switch s);
public abstract void off(Switch s);
}
//打开状态
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("已经关闭!");
}
}
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();
}
}
使用环境类实现状态转换
屏幕放大镜效果
三种状态:正常状态、二倍放大、四倍放大
//屏幕类
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("四倍大小!");
}
}
class Client {
public static void main(String args[]) {
Screen screen = new Screen();
screen.onClick();
screen.onClick();
screen.onClick();
}
}
优点:
- 封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
- 将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
- 允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
- 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点:
- 状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
- 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
- 状态模式对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
适用场景:
- 对象的行为依赖于它的状态(如某些属性值),状态的改变将导致行为的变化。
- 在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,并且导致客户类与类库之间的耦合增强。