设计模式(十五)状态模式

刘望舒 刘望舒     2022-08-28     595

关键词:

相关文章
设计模式系列

前言

建议在阅读本文前先阅读设计模式(十一)策略模式这篇文章,虽说状态模式和策略模式的结构几乎是相同的,但是它们所解决的问题是不同的,读完这两篇文章你就会有了答案。

1.状态模式定义

状态模式定义

定义:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

状态模式UML图

技术分享

在享元模式中有如下角色:

  • Context:环境角色,定义客户端需要的接口,并且负责具体状态的切换。
  • State:抽象状态角色,可以是抽象类或者接口,负责对象状态定义,并封装了环境角色。
  • ConcreteState:具体状态角色,实现抽象角色类,定义了本状态所要做的事情。

2.简单实现状态模式

拿用mp3听歌来说,mp3有四种基本状态,分别是开机、关机、上一首歌和下一首歌。如果我们要写一个对mp3进行控制的类,你可能会这样写,如下所示。

public class Mp3Controller {
    private static final int POWER_ON = 1;
    private static final int POWER_OFF = 2;
    private int state = POWER_OFF;
    public void powerOn() {
        if (state == POWER_OFF) {
            System.out.println("开机");
        }
        state = POWER_ON;
    }
    public void powerOff() {
        if (state == POWER_ON) {
            System.out.println("关机");
        }
        state = POWER_OFF;
    }
    public void preSong() {
        if (state == POWER_ON) {
            System.out.println("上一首歌");
        }
    }
    public void nextSong() {
        if (state == POWER_ON) {
            System.out.println("下一首歌");
        }
    }
}

在powerOn和powerOff方法中我们会将state置为相应的状态,在preSong和nextSong方法中,首先要判断当前mp3的state,如果是POWER_OFF,则不做任何处理,写到这里你可能会觉得实现很简单啊。那么我再添加些状态,比如待机状态、休眠状态、亮屏状态等等,顺便再添加些功能,比如调大音量、调小音量、降噪等。这样你实现起来,就会发现你会定义很多种状态,在功能中可能要用到多个条件语句进行判断,这会使得代码变得臃肿。
状态模式就是为了解决这一问题,将多个条件语句去掉,使得代码更加清晰,下面来进行实现。

抽象状态角色

public interface Mp3State {
    //开机
    public void powerOn();
    //关机
    public void powerOff();
    //上一首歌曲
    public void preSong();
    //下一首歌曲
    public void nextSong();
}

接口Mp3State中定义了四种功能,接下来我们来实现Mp3State。

具体状态角色

我们先来实现开机状态,代码如下所示。

public class PowerOnState implements Mp3State {
    @Override
    public void powerOn() {
        System.out.println("已开机");
    }
    @Override
    public void powerOff() {
        System.out.println("关机");
    }
    @Override
    public void preSong() {
        System.out.println("上一首歌");
    }
    @Override
    public void nextSong() {
        System.out.println("下一首歌");
    }
}

比较特殊的是powerOn方法中,打印了“已开机”,因为在PowerOnState 状态下进行开机操作是多此一举的。
接着实现关机状态:

public class PowerOffState implements Mp3State {
    @Override
    public void powerOn() {
        System.out.println("开机");
    }
    @Override
    public void powerOff() {
    }
    @Override
    public void preSong() {
    }
    @Override
    public void nextSong() {
    }
}

在关机状态中只实现了powerOn方法,其他的方法都是空实现。

环境角色

public class Context {
    private Mp3State mp3State;
    public void setMp3State(Mp3State mp3State){
        this.mp3State=mp3State;
    }
    public void powerOn(){
        mp3State.powerOn();
        setMp3State(new PowerOnState());
    }
    public void powerOff(){
        mp3State.powerOff();
        setMp3State(new PowerOffState());
    }
    public void preSong(){
        mp3State.preSong();
    }
    public void nextSong(){
        mp3State.nextSong();
    }
}

Context 中定义了setMp3State方法,用来设定状态,其中powerOn方法中会调用setMp3State方法将状态置为PowerOffState,同理powerOff中将状态置为PowerOffState。

客户端调用

public class Client {
    public static void main(String[] args){
        Context context=new Context();
        context.setMp3State(new PowerOffState());
        context.preSong();
        context.powerOn();
        context.nextSong();
        context.powerOff();
    }
}

我们只需要先设定mp3的初始状态,就可以调用各种功能方法了,不需要再考虑功能和状态之间的关系。输出结果为:
开机
下一首歌
关机

虽然这个例子的代码很简单,这里还是给出UML图,如下所示。
技术分享

3.状态模式的使用场景和优缺点

优点

  • 避免了过多的条件语句,使得结构更清晰,提高代码的可维护性。
  • 每个状态都是一个子类,方便增加和修改状态。
  • 状态被放置到类的内部,外部调用不需要知道类的内部如何实现状态和行为的变换。

缺点

  • 完全使用状态模式,可能会导致子类会过多。

使用场景

  • 代码中包含大量与对象状态有关的条件语句。
  • 对象的行为依赖着状态,并且行为随着状态的改变而改变。

github源码

参考资料
《大话设计模式》
《设计模式之禅》
《Android源码设计模式》


欢迎关注我的微信公众号,第一时间获得博客更新提醒,以及更多成体系的Android相关原创技术干货。
扫一扫下方二维码或者长按识别二维码,即可关注。

技术分享













java23种设计模式十五(连载)(代码片段)

备忘录模式备忘录模式又称备份模式、标记模式。顾名思义,其就是在某一时刻保存当前状态,作为备份,以便下次可以使用,或者恢复到上一次的状态。备忘录模式分为三个角色:普通类:用于定义要备... 查看详情

gof设计模式(十五)-观察者模式(代码片段)

前言观察者模式(ObserverPattern)指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。观察者模式属于行为型模式。  模式所涉及的角色抽象主题&#x... 查看详情

java23种设计模式十五(连载)(代码片段)

备忘录模式备忘录模式又称备份模式、标记模式。顾名思义,其就是在某一时刻保存当前状态,作为备份,以便下次可以使用,或者恢复到上一次的状态。备忘录模式分为三个角色:普通类:用于定义要备... 查看详情

设计模式(十五)——模板方法模式

设计模式(十五)——模板方法模式一、模板方法模式简介1、模板方法模式简介    模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,模板方法使得子类可以不改变一个算法的结构即可重... 查看详情

设计模式(十五)——桥接模式

1.描述 将桥接部分与他的实现部分分离,是他们都可以独立的变化。2.模式的使用·抽象(Abstraction):是一个抽象类,该抽象类含有Implementor的声明,即维护一个Implementor类型对象。·实现者(Implementor):实现者是一个接口或抽象... 查看详情

设计模式(十五):原型模式

...lone(),但一直没用过,也不知道怎么用。直到学习了原型设计模式才明白,他就是克隆方法,专门用来复制对象的。虽然到目前为止还没真正在项目中用到,但克隆方法还是挺有用的,它为我们创建相同对象带来了很大的便利,... 查看详情

设计模式学习笔记(十五:组合模式)

1.1概述 将对象组合成树形结构以表示“部分-整体”的层次结构。组合(Composite)使用户对单个对象和组合对象的使用具有一致性。这就是组合模式的定义。 如果一个对象包含另一个对象的引用,称这样的对象为组合对... 查看详情

设计模式之十五:訪问者模式(visitorpattern)

訪问者模式(VisitorPattern)是GoF提出的23种设计模式中的一种,属于行为模式。据《大话设计模式》中说算是最复杂也是最难以理解的一种模式了。  定义(源于GoF《DesignPattern》):表示一个作用于某对象结构中的各元素的操... 查看详情

设计模式之桥接模式优秀的程序结构(十五)

咱不要多,就一个隐身技能,嘿嘿嘿定义桥接模式(bridge):在软件系统中,某些由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种"多维度的变化"?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,... 查看详情

第十五章软件架构之设计模式——单例模式

单例模式:   分析:      1、单例模式,从字面意思上理解,“单例”,即只有唯一一个实例,通常情况下,定义一个类,然后通过new ClassName()方式来产生具体对象,然而这样,破坏了一个类... 查看详情

软件设计模式学习(十五)享元模式(代码片段)

当系统中存在大量相同或相似的对象时,享元模式是一种较好的解决方案,它通过共享技术实现相同或相似的细粒度对象的复用,从而节约内存空间。享元模式提供了一个享元池用于存储已经创建好的享元对象,并通过享元工厂... 查看详情

23种设计模式(十五)——适配器模式接口隔离

文章目录意图什么时候使用适配器真实世界类比适配器模式的实现适配器模式的优缺点亦称:封装器模式、Wrapper、Adapter意图将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器。什... 查看详情

解决gans训练中模式崩塌/训练崩溃的十五个方法

...经常会出现模式崩塌/训练崩溃的问题,这篇博客总结了十五个解决模式崩塌/训练崩溃的方法,建议收藏!目录什么是模式崩塌现象?为什么会出现模式崩塌?方法一:归一化 查看详情

scala入门到精通——第十五节caseclass与模式匹配

本节主要内容模式匹配的类型for控制结构中的模式匹配option类型模式匹配1.模式的类型1常量模式objectConstantPattern{defmain(args:Array[String]):Unit={//注意,以下定义的是一个函数//函数的返回值利用的是模式匹配后的结果作为其返回值//... 查看详情

css学习(十五)-css颜色模式css颜色透明度

一、理论:1.CSS3颜色模式a.RGBA颜色模式,在RGB基础上加了控制alpha透明度的参数b.HSL颜色模式:色调饱和度亮度c.HSLA颜色模式:A值取于0-1之间,值越大,透明度越低2.RGBA/HSLA滤镜格式a.需要用转换工具才能在ie8及以下版... 查看详情

第十五讲:桥接模式

//违反了开放封闭的原则//如果增加了一种发动机规格,Bus又要修改了.Car的代码也要修改.//另外如果接口定义的发动机规格Bus没有,为了保持接口的统一性不得不给Bus一个空的实现.这也是非常不好的.  Car(抽象)持有Engine(发动... 查看详情

第二十五讲:命令模式

Command:Command.Client:MainClass对于每一个行为都创建一个Command的实现子类:AppleCommand.//命令类必须包含调用方,是谁在卖,是Peddler在卖.所以命令类需要持有Peddler的引用.Invorker:执行Command对象.被调用者:Peddler,商贩被调用,所以需要持有一... 查看详情

scala基础(十五):scala模式匹配(代码片段)

1 变量声明中的模式match中每一个case都可以单独提取出来,意思是一样的.应用案例val(x,y)=(1,2)val(q,r)=BigInt(10)/%3//说明q=BigInt(10)/3r=BigInt(10)%3valarr=Array(1,7,2,9)valArray(first,second,_*)=arr//提出arr的前两个元素println(first,second 查看详情