状态模式(行为型)

来源:互联网 发布:wow台服数据库 编辑:程序博客网 时间:2024/06/16 02:10

  在很多种情况下,一个对象的行为取决于一个或多个动态变化的属性,这些属性称为状态。例如水,液态时可以流动,气态时可以扩散,结冰时成了固态,这时既不能流动也不能扩散。例如使用键盘输入字符,当Caps Lock开启时,处于大写状态,这时输入的是大写字母,当Caps Lock关闭时,输入的是小写字母。例如用手机打电话,有信号并且话费充足时,才可以打电话,当没信号或者是处于欠费状态,就无法打电话了。
  上面的例子都很接近于生活,浅显易懂。如果要对上面的这些逻辑进行编码实现,其实用if…else就可以,但当涉及的状态和行为很多时,这一大坨的代码就会变得很繁杂,维护起来很费力,也不利于扩展,当有新的状态时,就要对原来的代码进行比较大的改动,这时我们就需要考虑使用状态模式来让我们的代码变得更优雅,便于维护,便于扩展。
定义:允许一个对象在其内部状态改变时改变他的行为。
结构

  • Context:环境角色,定义客户端需要的接口,并且负责具体状态的切换
  • State:抽象状态角色,为接口或者抽象类,负责对状态定义,并且封装环境角色以实现状态切换。
  • ConcreteState:具体的状态角色,有两个职责:本状态的行为(本状态下要做的事情,以及本状态如何过渡到其他状态)管理和趋向状态处理;

适用场景

  • 行为随状态改变而改变,行为受状态约束的情况下
  • 条件、分支判断语句的替代者:在程序中大量使用switch或者if判断语句导致程序结构不清晰,逻辑混乱,使用状态模式可以很好地避免这一问题,通过扩展子类实现条件的判断处理

UML
这里写图片描述

下面就是状态模式的代码实现,这里以手机在有无话费状态下打电话为例子:
Context:本例中即为Class Phone

public class Phone {    private State hasPhoneCharge;    private State phoneArrears;    private State nowState;    //初始状态,该手机还剩下1块钱话费    private int phoneBill=1;    public State getHasPhoneCharge() {        return hasPhoneCharge;    }    public Phone setHasPhoneCharge(State hasPhoneCharge) {        this.hasPhoneCharge = hasPhoneCharge;        return this;    }    public State getPhoneArrears() {        return phoneArrears;    }    public Phone setPhoneArrears(State phoneArrears) {        this.phoneArrears = phoneArrears;        return this;    }    public State getNowState() {        return nowState;    }    public Phone setNowState(State nowState) {        this.nowState = nowState;        return this;    }    public int getPhoneBill() {        return phoneBill;    }    public Phone setPhoneBill(int phoneBill) {        this.phoneBill = phoneBill;        return this;    }    public void call(){        nowState.call();    }    public void payPhoneBill(int bill){        nowState.payPhoneBill(bill);    }}

State

public interface State {    void call();    void payPhoneBill(int bill);}

ConcreteState

public class PhoneArrears implements State{    Phone phone;    public PhoneArrears(Phone phone){        this.phone = phone;    }    @Override    public void call() {        System.out.println("您的手机已停机,无法拨打电话");    }    @Override    public void payPhoneBill(int bill) {        phone.setPhoneBill(phone.getPhoneBill()+bill);        System.out.println("成功充值"+bill+"元,目前话费余额"+phone.getPhoneBill());        if(phone.getPhoneBill()>0){            phone.setNowState(phone.getHasPhoneCharge());        }    }}public class HasPhoneCharge implements State{    Phone phone;    public HasPhoneCharge(Phone phone){        this.phone = phone;    }    @Override    public void call() {        System.out.println("通话成功,扣除一元话费");        phone.setPhoneBill(phone.getPhoneBill()-1);        if(phone.getPhoneBill()<=0){            System.out.println("您的手机因话费不足已停机,请充值");            phone.setNowState(phone.getPhoneArrears());        }    }    @Override    public void payPhoneBill(int bill) {        phone.setPhoneBill(phone.getPhoneBill()+bill);        System.out.println("成功充值"+bill+"元,目前话费余额"+phone.getPhoneBill());    }}

Test:运行一下看下打印出的是什么!

public class Test {    public static void main(String[] args) {        Phone phone = new Phone();        //这里不是错误,想偷懒就弄成链式调用了        phone.setHasPhoneCharge(new HasPhoneCharge(phone))             .setPhoneArrears(new PhoneArrears(phone))             .setNowState(phone.getHasPhoneCharge());        phone.call();        phone.call();        phone.payPhoneBill(10);        phone.call();    }}

总结:通过上面的代码我们可以看到,使用状态模式有效的避免被了过多的switch和if判断语句,降低了程序的复杂性,并且提高了可维护性。这样做程序的封装性也显得非常好,状态变换放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变换。状态模式有一个缺点就是子类太多(这也是大多数设计模式的共同缺点),如果一个事物有很多个状态使用状态模式就会有很多子类,尽管如此,也好过于去看一大坨的if…else语句,至少在逻辑上可以一目了然。


参考:https://github.com/nivance/DPModel/tree/master/src/dp/com/company/state
   https://segmentfault.com/a/1190000003818435
   http://dreamrunner.org/blog/2014/05/04/%E6%B5%85%E8%B0%88%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F13/

0 0