1.什么是状态模式?
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
举个例子:水在不同的温度下会有不同状态,也能表现出不同特性。看起来不一样,其实本质上它还是水
状态模式又叫状态对象,它是一种对象行为型模式。
2.状态模式结构
(1)Context(环境类):又叫上下文类,拥有多种状态的对象。由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类。在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象。
(2)State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以卸载抽象状态类中
(3)ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类的行为有所不同。
3.状态模式的实现
(1)环境类
/**
* 环境类
*/
public class Context {
private State state;//维持一个对抽象状态对象的引用
private int value;//其他属性值,该属性值的变化可能会导致对象的状态发生变化
public int getValue() {
return value;
}
//设置状态对象
public void setState(State state){
this.state = state;
}
public void request(){
//其他代码
state.handle();//调用状态对象的业务方法
//其他代码
}
public void changeState(){
//判断属性值,根据属性值进行状态转换
if(value == 0){
this.setState(new ConcreteState());
}else if(value == 1){
this.setState(new ConcreteState());
}
}
}
(2)抽象状态类
/**
* 抽象状态类
*/
public abstract class State {
//声明抽象业务方法,不同的具体状态类可以有不同的实现
public abstract void handle();
}
(3)具体状态类
/**
* 具体状态类
*/
public class ConcreteState extends State {
@Override
public void handle() {
//方法的具体实现代码
}
}
(4)状态变更
public class StateChange {
public void changeState(Context context){
//判断属性值,根据属性值进行状态转换
if(context.getValue() == 0){
context.setState(new ConcreteState());
}else if(context.getValue() == 1){
context.setState(new ConcreteState());
}
}
}
4.状态模式实例——银行账户随余额不同有不同状态
(1)账户状态类,充当抽象状态类
/**
* 账户状态类,充当抽象状态类
*/
public 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();
}
(2)正常状态类,充当具体状态类
/**
* 正常状态类,充当具体状态类
*/
public class NormalState extends AccountState {
public NormalState(Account acc){
this.acc = acc;
}
public NormalState(AccountState state){
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("正常状态,无需支付利息");
}
//状态转换
@Override
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("操作受限");
}
}
}
(3)透支状态类,充当具体状态类
/**
* 透支状态类,充当具体状态类
*/
public class OverdraftState extends AccountState {
public OverdraftState(Account acc){
this.acc = acc;
}
public OverdraftState(AccountState state){
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
acc.setBalance(acc.getBalance()-amount);
stateCheck();
}
@Override
public void computeInterest() {
System.out.println("计算利息");
}
@Override
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("操作受限");
}
}
}
(4)受限状态类,充当具体状态类
/**
* 受限状态类,充当具体状态类
*/
public class RestrictedState extends AccountState {
public RestrictedState(Account acc){
this.acc = acc;
}
public RestrictedState(AccountState state){
this.acc = state.acc;
}
@Override
public void deposit(double amount) {
acc.setBalance(acc.getBalance()+amount);
stateCheck();
}
@Override
public void withdraw(double amount) {
System.out.println("账号受限,取款失败");
}
@Override
public void computeInterest() {
System.out.println("计算利息");
}
@Override
public void stateCheck() {
if(acc.getBalance() > 0){
acc.setState(new NormalState(this));
}else if(acc.getBalance() > -2000){
acc.setState(new OverdraftState(this));
}
}
}
(5)银行账户,充当环境类
/**
* 银行账户,充当环境类
*/
public 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 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方法
}
public AccountState getState() {
return state;
}
public void setState(AccountState state) {
this.state = state;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
(6)客户端类
public class Client {
public static void main(String[] args) {
Account account = new Account("段誉",0.0);
account.deposit(1000);
account.withdraw(2000);
account.deposit(3000);
account.withdraw(4000);
account.withdraw(1000);
account.computeInterest();
}
}
(7)路径及结果
5.共享状态
在有些情况下,多个环境对象可能需要共享一个状态,如果希望在系统中实现多个环境对象共享一个或多个状态对象,那么需要将这些状态对象定义为环境类的静态成员变量。
实例——两个开关要么同时开,要么同时关
(1)开关类
/**
* 开关类
*/
public class Switch {
private static SwitchState currentState,onState,offState;//定义3个静态的状态对象
private String name;
public Switch(String name){
this.name = name;
onState = new OnState();
offState = new OffState();
currentState = onState;
}
public void setState(SwitchState state){
currentState = state;
}
public static SwitchState getState(String type){
if(type.equalsIgnoreCase("on")){
return onState;
}else {
return offState;
}
}
//打开开关
public void on(){
System.out.println(name);
currentState.on(this);
}
//关闭开关
public void off(){
System.out.println(name);
currentState.off(this);
}
}
(2)抽象状态类
/**
* 抽象状态类
*/
public abstract class SwitchState {
public abstract void on(Switch s);
public abstract void off(Switch s);
}
(3)打开状态类
/**
* 打开状态类
*/
public class OnState extends SwitchState {
@Override
public void on(Switch s) {
System.out.println("已经打开");
}
@Override
public void off(Switch s) {
System.out.println("关闭");
s.setState(s.getState("off"));
}
}
(4)关闭状态类
/**
* 关闭状态类
*/
public class OffState extends SwitchState {
@Override
public void on(Switch s) {
System.out.println("打开");
s.setState(s.getState("on"));
}
@Override
public void off(Switch s) {
System.out.println("关闭");
}
}
(5)客户端
public 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();
}
}
(6)输出结果及路径
6.使用环境类实现状态转换
客户端其实不关心状态类,将状态的转换工作交给具体状态类或环境类来完成,对客户端透明。
实例——第一次单机“放大镜”,屏幕放大一倍。再单击,再放大一倍。再点击,恢复原来大小
(1)屏幕类:环境类
/**
* 屏幕类:环境类
*/
public class Screen {
//枚举所有的状态,currentState表示当前状态
private ScreenState currentState,normalState,largeState,largestState;
public Screen(){
this.normalState = new NormalState();//正常状态对象
this.largeState = new LargeState();//二倍放大状态对象
this.largestState = new LargestState();//四倍大状态对象
this.currentState = normalState;//设置初始状态
this.currentState.display();
}
public void setState(ScreenState state){
this.currentState = state;
}
//单机事件处理方法,封装了对状态类中业务方法的调用的状态的转换
public void onClick(){
if (this.currentState == normalState){
this.setState(largeState);
this.currentState.display();
}else if (this.currentState == largeState){
this.setState(largestState);
this.currentState.display();
}else if (this.currentState == largestState){
this.setState(normalState);
this.currentState.display();
}
}
}
(2)屏幕状态类:抽象状态类
/**
* 屏幕状态类:抽象状态类
*/
public abstract class ScreenState {
public abstract void display();
}
(3)正常状态类:具体状态类
/**
* 正常状态类:具体状态类
*/
public class NormalState extends ScreenState {
@Override
public void display() {
System.out.println("正常大小");
}
}
(4)二倍状态类:具体状态类
/**
* 二倍状态类:具体状态类
*/
public class LargeState extends ScreenState {
@Override
public void display() {
System.out.println("二倍大小!");
}
}
(5)四倍状态类:具体状态类
/**
* 四倍状态类:具体状态类
*/
public class LargestState extends ScreenState {
@Override
public void display() {
System.out.println("四倍大小!");
}
}
(6)客户端
public class Client {
public static void main(String[] args) {
Screen screen = new Screen();
screen.onClick();
screen.onClick();
screen.onClick();
}
}
(7)输出结果及路径
7.状态模式优缺点
优:
(1)状态模式封装了状态的转换规则,将转换代码封装在环境类中或者具体状态类中,而不是分散在业务代码里
(2)状态模式将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为
(3)状态模式允许状态逻辑与状态对象合成一体
(4)让多个环境对象共享一个状态对象
缺:
(1)增加类和对象个数,增加开销
(2)设计较为复杂
8.使用环境
(1)对象的行为依赖于它的状态
(2)包含大量与对象状态相关的条件语句