状态模式-对象状态及其转换

某信用卡业务系统,银行账户存在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);
    }

    @Override
    public void deposit(double money) {
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        userAccount.setBalance(userAccount.getBalance() - money);
    }

    @Override
    public void payInterest() {

    }

    @Override
    public String getState() {
        return "正常";
    }

}

public class OverdraftCardState extends CardState{

    public OverdraftCardState(UserAccount userAccount) {
        super(userAccount);
    }

    @Override
    public void deposit(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() - money);
    }

    @Override
    public void payInterest() {
        System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
        userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
    }

    @Override
    public String getState() {
        return "透支";
    }
}

public class LimitationCardState extends CardState{

    public LimitationCardState(UserAccount userAccount) {
        super(userAccount);
    }

    @Override
    public void deposit(double money) {
        payInterest();
        userAccount.setBalance(userAccount.getBalance() + money);
    }

    @Override
    public void withdraw(double money) {
        System.out.println("该账户已受限,不能取款");
    }

    @Override
    public void payInterest() {
        System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
        userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
    }

    @Override
    public 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{

    @Override
    public void on(LightSwitch lightSwitch) {
        System.out.println("灯泡已打开");
        System.out.println("--------------");
    }

    @Override
    public void off(LightSwitch lightSwitch) {
        System.out.println("关闭成功");
        LightSwitch.changeLightState("off");
        System.out.println("--------------");
    }

}

public class OffLightState extends LightState{

    @Override
    public void on(LightSwitch lightSwitch) {
        System.out.println("打开成功");
        LightSwitch.changeLightState("on");
        System.out.println("--------------");
    }

    @Override
    public 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)代码中包含大量与对象状态有关的条件语句。

相关推荐
吾与谁归in8 分钟前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式
吾与谁归in9 分钟前
【C#设计模式(14)——责任链模式( Chain-of-responsibility Pattern)】
设计模式·c#·责任链模式
闲人一枚(学习中)16 分钟前
设计模式-创建型-原型模式
设计模式
Iced_Sheep1 小时前
干掉 if else 之策略模式
后端·设计模式
哪 吒8 小时前
最简单的设计模式,抽象工厂模式,是否属于过度设计?
设计模式·抽象工厂模式
Theodore_10228 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
转世成为计算机大神11 小时前
易考八股文之Java中的设计模式?
java·开发语言·设计模式
小乖兽技术12 小时前
23种设计模式速记法
设计模式
小白不太白95014 小时前
设计模式之 外观模式
microsoft·设计模式·外观模式
小白不太白95014 小时前
设计模式之 原型模式
设计模式·原型模式