State
意图
允许一个对象在其内部状态改变时改变他的行为,对象看起来似乎修改了他的类
适用性
1、一个对象那个的行为取决于他的状态,并且它必须在运行时刻根据状态改变他的行为
2、一个操作中含有庞大的多分支条件语句,且这些分支依赖于该对象的状态。这个状态通常用一个或多个枚举常量表示。通常,有多个操作包涵这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况 将对象的状态作为一个对象,这一对象可以不依赖其他对象而独立变化
结构
Context
环境类,又称为上下文类,它是拥有多种状态的对象。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
State
用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中。
ConcreteState
它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
实现
银行账户,分为三个状态
1、正常状态(余额大与0),用户可存款也可取款;
2、透支状态(0 > 余额 > -2000),用户可存款也可取款,但需要按天计算利息;
3、受限状态(余额 = -2000),用户只能存款,不能取款,同时也将按天计算利息;
我们根据不同的状态执行不同的操作
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:33
*/
public class Account {
private AccountState state;
private String owner;
private double balance = 0;
public Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
this.state = new NormalState(this);
System.out.println(this.owner + "开户成功,余额为" + balance);
System.out.println("---------------------------------------------");
}
public double getBalance() {
return 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);
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);
System.out.println("现在余额为" + this.balance);
System.out.println("现在帐户状态为" + this.state.getClass().getName());
System.out.println("---------------------------------------------");
}
public void computeInterest() {
state.computeInterest();
}
}
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:33
*/
public abstract class AccountState {
protected Account account;
public abstract void deposit(double amount);
public abstract void withdraw(double amount);
public abstract void computeInterest();
public abstract void stateCheck();
}
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:40
*/
public class NormalState extends AccountState {
public NormalState(Account account) {
this.account = account;
}
public NormalState(AccountState accountState) {
this.account = accountState.account;
}
@Override
public void deposit(double amount) {
account.setBalance(account.getBalance() + amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
account.setBalance(account.getBalance() - amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("正常状态,无需支付利息!");
}
@Override
public void stateCheck() {
if (account.getBalance() > -2000 && account.getBalance() <= 0) {
account.setState(new OverdraftState(this));
} else if (account.getBalance() == -2000) {
account.setState(new RestrictedState(this));
} else if (account.getBalance() < -2000) {
System.out.println("操作受限!");
}
}
}
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:40
*/
public class OverdraftState extends AccountState {
public OverdraftState(AccountState accountState) {
this.account = accountState.account;
}
@Override
public void deposit(double amount) {
account.setBalance(account.getBalance() + amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
account.setBalance(account.getBalance() - amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("需支付利息!");
}
@Override
public void stateCheck() {
if (account.getBalance() > 0) {
account.setState(new NormalState(this));
} else if (account.getBalance() == -2000) {
account.setState(new RestrictedState(this));
} else if (account.getBalance() < -2000) {
System.out.println("操作受限!");
}
}
}
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:40
*/
public class RestrictedState extends AccountState {
public RestrictedState(AccountState accountState) {
this.account = accountState.account;
}
@Override
public void deposit(double amount) {
account.setBalance(account.getBalance() + amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
System.out.println("帐号受限,取款失败");
}
@Override
public void computeInterest() {
System.out.println("需支付利息!");
}
@Override
public void stateCheck() {
if (account.getBalance() > 0) {
account.setState(new NormalState(this));
} else if (account.getBalance() > -2000) {
account.setState(new OverdraftState(this));
}
}
}
package state;
/**
* @Author fitz.bai
* @Date 2018/9/10 16:30
*/
public class Client {
public static void main(String args[]) {
Account acc = new Account("Fitz", 0.1);
acc.deposit(1000);
acc.withdraw(2000);
acc.deposit(3000);
acc.withdraw(4000);
acc.withdraw(1000);
acc.computeInterest();
}
}
/**Fitz开户成功,余额为0.1
---------------------------------------------
Fitz存入金额1000.0
现在余额为1000.1
现在的帐户状态为state.NormalState
---------------------------------------------
Fitz取出2000.0
现在余额为-999.9
现在帐户状态为state.OverdraftState
---------------------------------------------
Fitz存入金额3000.0
现在余额为2000.1
现在的帐户状态为state.NormalState
---------------------------------------------
Fitz取出4000.0
现在余额为-1999.9
现在帐户状态为state.OverdraftState
---------------------------------------------
Fitz取出1000.0
操作受限!
现在余额为-2999.9
现在帐户状态为state.OverdraftState
---------------------------------------------
需支付利息!*/
其实状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象可以让环境对象拥有不同的行为,而状态转换的细节对于客户端而言是透明的,方便了客户端的使用。在实际开发中,状态模式具有较高的使用频率,在工作流和游戏开发中状态模式都得到了广泛的应用,例如公文状态的转换、游戏中角色的升级等。
优点
1、封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中。
2、将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为。
3、允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起。
4、State对象可被共享,可以减少系统中对象的个数。
缺点
1、状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大。
2、 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度。
3、状态模式对“开闭原则”的支持并不太好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。