状态模式(State Design Pattern)

来源:互联网 发布:c语言产生随机数的方法 编辑:程序博客网 时间:2024/06/08 06:34

以此回顾《设计模式之禅》及其他设计模式书籍、视频中的状态模式。

什么是状态模式?

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. (当一个对象的内在状态改变时,允许它修改自己的行为。这个对象看起来像是改变了类)

状态模式有3个重要部分。(1)Context(Account)(2)State (3)Concrete State

如何理解状态模式中的Context?

Context(Account):Maintains an instance of a ConcreteState subclass that defines current state。持有一个定义了当前状态的实例,这个“当前状态”就是一个具体状态(ConcreteState),实现了State,是State的子集。

如何理解状态模式中的State?

State:Defines an interface for encapsulating the behavior associated with a particular state of the context. 定义了一个接口,接口里包含了context持有的具体状态要实现的方法。

如何理解状态模式中的Concrete State?

Concrete State:Each subclass implements a behavior associated with a state of context。具体状态里实现了接口中定义的方法。

理解了这3个部分,再看定义,不难发现,状态模式其实就是说,当context持有的某个状态(concrete state)改变时,它的行为也发生了改变(因为每个concrete state对state接口中定义的方法的实现不同,行为也就不同)。

例如,把电梯抽象出4个状态,分别是运行、停止、开门、关门,则它的状态模式UML类图为:


使用状态模式的好处?

(1)结构清晰。

避免了switch...case或者if...else的使用,减少了程序的负责行,使逻辑清晰。试想,刚才的电梯的例子,假设不用状态模式,则会变成这样,直接上代码:

public void close() {    //电梯在什么情况下能关闭    switch(state) {        //如果此时门开着        case OPEN_DOOR_STATE:             //可以关门            this.closeDoor();            this.setState(CLOSE_DOOR_STATE);            break;         //如果此时门关着         case CLOSE_DOOR_STATE:            //do nothing            break;         //如果此时电梯正在运行         case RUN_STATE:             //do nothing             break;         //如果此时电梯停止运行         case STOP_STATE:             //do nothing             break;    }}//同理,开门public void openDoor() {    //电梯什么情况下能开门    switch(state) {         case XXX:break;         case XXX:break;         case XXX:break;         case XXX:break;    }}//同理,运行public void run() {    //电梯什么情况下能运行    switch(state) {         case XXX:break;         case XXX:break;         case XXX:break;         case XXX:break;    }}//同理,停止public void stop() {    //电梯什么情况下能停止    switch(state) {         case XXX:break;         case XXX:break;         case XXX:break;         case XXX:break;    }}
显然,大量的switch case语句这个类过长,程序逻辑结构复杂,不容易理解,难以维护。

(2)遵循设计原则

状态模式很好的体现了开闭原则和单一职责原则,每个状态都是一个子类,只要增加子类,就能进行扩展,比如电梯的例子,如果想增加通电和断电状态,直接增加子类就可以实现,但是如果用上面的未使用状态模式的代码,就会又增加一大波判断,逻辑更加复杂,且破坏了开闭原则。

(3)封装性好

这也是状态模式的要求,将类的变换放置到类的内部来实现,外部的调用不知道类内部如何实现状态和行为的变换,符合迪米特法则(最少知道法则)。

如何使用状态模式?

用ATM机的例子(来自于视频:https://www.youtube.com/watch?v=MGEx35FjBuo)来讲一个状态模式的应用。

1、想想ATM机的所有可能状态。

(1)HasCard

(2)NoCard

(3)HasPin

(4)NoCash

2、想想人们使用ATM机的几种操作。

(1)InsertCard

(2)EjectCard

(3)InsertPin

(4)RequestCash

3、UML类图


4、具体代码

ATMState.java

public interface ATMState {    void insertCard();    void ejectCard();    void insertPin(int pinEntered);    void requestCash(int cashToWithdraw);}
ATMMachine.java

public class ATMMachine {    ATMState hasCard;    ATMState noCard;    ATMState hasCorrectPin;    ATMState atmOutOfMoney;    ATMState atmState;    int cashInMachine = 2000;    boolean correctPinEntered = false;    public ATMMachine() {        hasCard = new HasCard(this);        noCard = new NoCard(this);        hasCorrectPin = new HasPin(this);        atmOutOfMoney = new NoCash(this);        atmState = noCard;        if (cashInMachine < 0) {            atmState = atmOutOfMoney;        }    }    void setAtmState(ATMState newAtmState) {        atmState = newAtmState;    }    public void setCashInMachine(int newCashInMachine) {        cashInMachine = newCashInMachine;    }    public void insertCard() {        atmState.insertCard();    }    public void ejectCard() {        atmState.ejectCard();    }    public void requestCash(int cashToWithdraw) {        atmState.requestCash(cashToWithdraw);    }    public void insertPin(int pinEntered) {        atmState.insertPin(pinEntered);    }    public ATMState getYesCardState() {        return hasCard;    }    public ATMState getNoCardState() {        return noCard;    }    public ATMState getHasCorrectPinState() {        return hasCorrectPin;    }    public ATMState getAtmOutOfMoneyState() {        return atmOutOfMoney;    }}

HasCard.java

public class HasCard implements ATMState {    ATMMachine atmMachine;    public HasCard(ATMMachine atmMachine) {        this.atmMachine = atmMachine;    }    @Override    public void insertCard() {        System.out.println("You can't enter more than one card!");    }    @Override    public void ejectCard() {        System.out.print("Card ejected");        atmMachine.setAtmState(atmMachine.getNoCardState());    }    @Override    public void insertPin(int pinEntered) {        if (pinEntered == 1234) {            System.out.println("Correct Pin");            atmMachine.correctPinEntered = true;            atmMachine.setAtmState(atmMachine.getHasCorrectPinState());        } else {            System.out.println("Wrong Pin");            atmMachine.correctPinEntered = false;            System.out.println("Card ejected");            atmMachine.setAtmState(atmMachine.getNoCardState());        }    }    @Override    public void requestCash(int cashToWithdraw) {        System.out.println("Enter pin first");    }}

NoCard.java

public class NoCard implements ATMState {    ATMMachine atmMachine;    public NoCard(ATMMachine atmMachine) {        this.atmMachine = atmMachine;    }    @Override    public void insertCard() {        System.out.println("Please enter a pin");        atmMachine.setAtmState(atmMachine.getYesCardState());    }    @Override    public void ejectCard() {        System.out.println("Enter a card first");    }    @Override    public void insertPin(int pinEntered) {        System.out.println("Enter a card first");    }    @Override    public void requestCash(int cashToWithdraw) {        System.out.println("Enter a card first");    }}

HasPin.java

public class HasPin implements ATMState {    ATMMachine atmMachine;    public HasPin(ATMMachine atmMachine) {        this.atmMachine = atmMachine;    }    @Override    public void insertCard() {        System.out.println("You can't enter more than one card");    }    @Override    public void ejectCard() {        System.out.println("Card ejected");        atmMachine.setAtmState(atmMachine.getNoCardState());    }    @Override    public void insertPin(int pinEntered) {        System.out.println("Already entered pin");    }    @Override    public void requestCash(int cashToWithdraw) {        if (cashToWithdraw > atmMachine.cashInMachine) {            System.out.println("Don't have that cash");            System.out.println("Card ejected");            atmMachine.setAtmState(atmMachine.getNoCardState());        } else {            System.out.println(cashToWithdraw + " is provided by machine");            atmMachine.setCashInMachine(atmMachine.cashInMachine - cashToWithdraw);            System.out.println("Card ejected");            atmMachine.setAtmState(atmMachine.getNoCardState());            if (atmMachine.cashInMachine <= 0) {                atmMachine.setAtmState(atmMachine.getAtmOutOfMoneyState());            }        }    }}

NoCash.java

public class NoCash implements ATMState {    ATMMachine atmMachine;    public NoCash(ATMMachine atmMachine) {        this.atmMachine = atmMachine;    }    @Override    public void insertCard() {        System.out.println("We don't have money");    }    @Override    public void ejectCard() {        System.out.println("We don't have money, you didn't enter a card");    }    @Override    public void insertPin(int pinEntered) {        System.out.println("We don't have money");    }    @Override    public void requestCash(int cashToWithdraw) {        System.out.println("We don't have money");    }}

TestATMMachine.java

public class TestATMMachine {    public static void main(String[] args) {        ATMMachine atmMachine = new ATMMachine();        atmMachine.insertCard();        atmMachine.ejectCard();        atmMachine.insertCard();        atmMachine.insertPin(1234);        atmMachine.requestCash(2000);        atmMachine.requestCash(100);        atmMachine.insertCard();        atmMachine.insertPin(1234);    }}

什么时候使用状态模式,以及状态模式的注意事项?

(1)状态模式适用于某个对象当他的状态改变时,行为也有有较大变化的时候,考虑使用状态模式,但是对象的状态最好不要超过5个,否则会出现类爆炸,不好管理。

(2)当程序中有大量的if else 或者 switch case导致逻辑结构不清晰时,可以考虑状态模式。


0 0