状态模式(行为型)

  在很多种情况下,一个对象的行为取决于一个或多个动态变化的属性,这些属性称为状态。例如水,液态时可以流动,气态时可以扩散,结冰时成了固态,这时既不能流动也不能扩散。例如使用键盘输入字符,当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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值