状态模式

1 定义

状态模式(State Pattern):允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了其他的类.

2结构图

在这里插入图片描述
Context(环境类):环境类又称为上下文类,它是拥有多种状态的对象.由于环境类的状态存在多样性且在不同状态下对象的行为有所不同,因此将状态独立出去形成单独的状态类.

State(抽象状态类):它用于定义一个接口以封装与环境类的一个特定状态相关的行为,在抽象状态类中声明了各种不同状态对应的方法,而在其子类中实现类这些方法,由于不同状态下对象的行为可能不同,因此在不同子类中方法的实现可能存在不同,相同的方法可以写在抽象状态类中.

ConcreteState(具体状态类):它是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同.

3 代码实现

public class Context {

    private State state; //维持一个对抽象状态对象的引用
    private String value; //其他属性,该属性值的变化可能会导致对象状态发生变化

    public Context(String value){
        this.value = value;
        if(value.equals("A")){
            state = new ConcreteStateA(this);
        }else if("B".equals(value)){
            state = new ConcreteStateB(this);
        }
    }

    // 设置状态对象
    public void setState(State state){
        this.state = state;
    }

    public void request(String value){
        //其他代码
        this.value = value;
        state.handle(value);

    }

    public State getState() {
        return state;
    }
}
public abstract class State {

    protected Context context;

    //声明抽象业务方法,不同的具体状态类可以不同的实现
    public abstract void handle(String value);

}
public class ConcreteStateB extends State {

    public ConcreteStateB(Context context){
        this.context = context;
    }

    public ConcreteStateB(State state){
        this.context = state.context;
    }

    @Override
    public void handle(String value) {
        checkState(value);
        System.out.println("current value is "+value);
        System.out.println("current state is " + context.getState().getClass().getName());
        System.out.println("================");
    }

    public void checkState(String value){
        if(value.equals("A")){
            this.context.setState(new ConcreteStateA(this));
        }
    }

}

public class ConcreteStateA extends State {

    public ConcreteStateA(Context context){
        this.context = context;
    }

    public ConcreteStateA(State state){
        this.context = state.context;
    }

    @Override
    public void handle(String value) {
        checkState(value);
        System.out.println("current value is "+value);
        System.out.println("current state is " + context.getState().getClass().getName());
        System.out.println("================");
    }

    public void checkState(String value){
        if(value.equals("B")){
            this.context.setState(new ConcreteStateB(this));
        }
    }

}

public class Client {

    public static void main(String[] args) {
        Context context = new Context("A");
        context.request("A");
        context.request("B");
    }

}

4 具体案例

银行账户,账户存在三种状态,在不同状态下,存在不同的行为:
1)如果账户中余额大于等于0,则账户的状态为正常(Normal State),此时用户既可以向该账户存款也可以从该账户取款
2)如果账户中余额小于0,并且大于-2000,则账户的状态为透支状态(Overdraft State),此时账户既可以向该账户存款也可以从该账户取款,但需要按天计算利息
3)如果账户中余额等于-2000,那么账户的状态为受限状态(Restricted State),此时用户只能向该账户存款,不能在从中取款,同时也将按天计算利息
4)根据余额的不同,以上状态可发生相互转换
具体代码实现如下:

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 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());
    }

    public void computeInterest(){
        state.computeInterest();//调用状态对象的computeInterest()方法
    }

}
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();


}
public class NormalState extends AccountState {

    public NormalState(AccountState state){
        this.acc = state.acc;
    }

    public NormalState(Account acc){
        this.acc = acc;
    }

    @Override
    public void deposit(double amount) {
        acc.setBalance(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("操作受限!");
        }
    }
}
public class OverdraftState extends AccountState {

    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) {

    }

    @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("操作受限!");
        }
    }
}
public class RestrictedState extends AccountState {

    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));
        }

    }
}

public 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();
    }
}

5 优缺点及适用场景

主要优点:
1)封装了状态的转换规则,在状态模式中可以将状态的转换代码封装在环境类或者具体状态类中,可以对状态转换代码进行集中管理,而不是分散在一个个业务方法中.
2)将所有与某个状态有关的行为放到一个类中,只需要注入一个不同的状态对象即可使环境对象拥有不同的行为
3)允许状态转换逻辑与状态对象合成一体,而不是提供一个巨大的条件语句块,状态模式可以让我们避免使用庞大的条件语句来将业务方法和状态转换代码交织在一起.
4)可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数

主要缺点:
1)状态模式的使用必然会增加系统中类和对象的个数,导致系统运行开销增大
2)状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱,增加系统设计的难度.
3)状态模式对"开闭原则"的支持并不太友好,增加新的状态类需要修改那些负责状态转换的源代码,否则无法转换到新增状态;而且修改某个状态类的行为也需要修改对应类的源代码

适用场景:
1)对象的行为依赖于它的状态,状态的改变将导致行为的变化
2)在代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便的增加和删除状态,并且导致客户类与类库之间的耦合增强.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值