Java设计模式之状态模式(State)

java的设计模式一共有24种,我自己经常用到的是单例模式,观察者模式,策略模式。我也有去了解建造者模式,工厂模式等,但是看了也就忘了,这就说明了,看了一定要马上拿来用!项目里到处都可以进行改造,信手拈来就是用,这样一定会让自己很难忘的,下次在合适的契机也能想到用什么模式。

话不多说,说说我为什么需要来研究状态模式(State)。最近遇到一个需求,两个相似度很高的界面,(首先遇到这种我就会想能不能用一个界面把它解决完),好的!暂且让他们用一个界面来实现吧!如果是这样的话,那么一个界面就有两种状态,一种是A状态,一种是B状态。当我点击A的时候调用A模式,点击B的时候调用B模式。

思考:涉及到状态判断,那么也就说我需要写很多的 if else 判断,(随后我老老实实的写了3次判断在代码里,准备第4次的时候,我停下了。感觉自己做的事很蠢,于是我急于寻求一个设计模式来帮我走出困境),然后我晃眼一看,我觉得状态模式很适合我!

但我在网上看了很多讲解状态模式的博客,不是说看不懂,但就是真的让人失望,写得很不走心,很敷衍,讲得不明不白的,还有的甚至和策略模式混为一谈,实在是滑稽。

所以我决定自己来写一篇自己可以随时拿出来复习的博客,去其糟粕,取其精华,写这篇博客的目的是能够让自己明白为前提。

注意:本文的示例代码是用kotlin编写,语法简单,不存在高级写法,易懂!

 

我将从以下几点来说明状态模式:

(一).状态模式适用于哪种情况

(二).如何使用状态模式

(三).状态模式的好处

(四).状态模式的缺点

(五).状态模式和策略模式的差别

 

一.状态模式适合用于哪种情况

     状态模式适用于,当一个对象的行为取决于它的状态,并且必须根据它在运行时刻根据状态改变它的行为时。这种情况适合使用状态模式。

上面的说法比较官方,现在我用白话来解释一下。(不保证每个人都能懂哈)

假设:一个集美貌与才华的美少男,他叫XMan,这个男子给自己安排了一天花样很多的行程,我们就暂且理解XMan是个日程怪吧。

他早上7:00-9:00,有这么几个事要做:洗漱,吃饭,看早报,遛狗。

然后9:00-12:00,他这个时间段,唯一要做的是:在公司认认真真的划水摸鱼。

随后12:00-14:00,他需要一个美容午觉。

下午14:00-16:00,他要和国际大佬铁柱开会。

下午16:00-18:00,他要做的事是:喝下午茶,写日报。

晚上18:00-21:00,这个时间点:和XWoman约会。

你要写一个这样的XMan类出来,处理他一天要做的这些事,你是不是第一反应是:啊,我要枚举,我要if,我要else,我还有switch,我还有.....行了,你闭嘴吧。

这些你要是用if else,switch来判断,你确定不会疯掉吗?(不管你会不会,反正我会。)

这种情况下,可以使用状态模式来做处理。

 

二.如何使用状态模式

    1.状态模式需要做什么

我先来介绍一下,使用状态模式大概要用到的几个东西吧。你想要开车,你总得知道这个车是什么车吧,箱式的,还是小轿车,是自动挡,还是手动挡。

万一你觉得这个对你来说复杂了,你不乐意玩儿,或者玩不了,就可以趁早换车(换设计模式),不用浪费时间。(多哔哔了两句,下面开始表演)

我们大概会新建以下几个类:

  XManAllState:抽象状态

  XManStateController:控制你的状态的一个控制器

  XManActivity:调用状态的Activity

  Xman7To9:状态7:00-9:00的状态类

  XMan9To12:状态9:00-12:00的状态类

  XMan12To14:状态12:00-14:00的状态类

....这里你有几个状态,就有几个状态类。

我是属于宁愿多写几个类,也不愿意在一个方法里写过多代码的人,不知道大家还记不记得一句话,一个方法过长,内容过多,你就应该思考你的代码是不是有坏掉了的味道。

现在你就大概知道状态模式需要写什么类,而这些类又分别是拿来做什么的,那接下来就是具体的实现了。

2.具体做法(这里的几个类都是相互牵扯的,看的时候最好敲一下,否则小菜看完后可能会不明白)

步骤一:新建一个抽象状态类,里面有一个抽象方法。(这里传的参数是我们的控制器类,以防代码内部报错,你可以先建一个控制器空类,这样可以跟着我一步一步走)

abstract class XManAllState {

    abstract fun XManTimeState(XManController:XManStateController)
}

步骤二:新建一个状态类,继承抽象状态类,实现抽象方法,这里是7:00-9:00这个时段的状态。(建议把所有的状态都先建立好,具体实现后面会一步一步的来)

class Xman7To9:XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
       
    }

}

步骤三:走到这里,那么前面两个步骤你已经完成了。现在是很重要的一个步骤了,创建XMan日程的控制器类。

class XManStateController {

    //时间的初始化是7-9之间,自己任意设置
    var currentState:XManAllState=Xman7To9()

    //时间在Activity给它赋值,状态类用它来判断时间是否满足状态
    var Hour:Int = 0

    //设置当前的状态
    fun XManTimeState(){
        currentState.XManTimeState(this)
    }

}

步骤四:具体实现各个状态类

上面我们已经把控制器写好了,现在要做的就是把各个状态类的具体实现写出来。我还是以7:00-9:00的状态来打个样儿吧。

class Xman7To9:XManAllState(){
    override fun XManTimeState(XManController: XManStateController) {
        //做时间判断是在7-9之内的
        if (XManController.Hour<9&&XManController.Hour>=7){
            do7To9()
        }else{
            //如果时间不在7-9之内,则修改当前的state为9-12状态类
            XManController.currentState=XMan9To12()
        }
    }

    //7-9之内的具体实现方法
    fun do7To9(){
        Log.i("XMan","XMan洗漱,吃饭,看早报,遛狗")
    }
}

接下来我再上一个9:00-12:00状态类的具体实现。(这样大家可以多看两个我的实现类,不会一个看完了还晕乎乎的)

class XMan9To12 :XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
        //这里我做个解释:因为我的Hour值是int类型,kotlin自动帮我把XManController.Hour>=9&&XManController.Hour<12 
        //这个判断转化成 XManController.Hour in 9..11 ,为什么是11,因为在int里,<12就是11
        if (XManController.Hour in 9..11){
            do9To12()
        }else{
            XManController.currentState=XMan12To14()
        }
    }

    fun do9To12(){
        Log.i("XMan","努力认真的划水摸鱼")
    }

}

接下来的的状态类就按照上面的依次来做就可以了。你可能会想,那我最后一个状态,也就是18:00-21:00如果时间不满足,怎么做呢?

我会给大家一个思路。下面的这个代码是我对于18:00-21:00状态的处理。

大家可以看到我对这个状态的处理就是,只处理了18:00-21:00的时间段。你可以根据自己的需要,对不在这个范围的情况做自己相应的处理。

class XMan18To21 :XManAllState(){

    override fun XManTimeState(XManController: XManStateController) {
        if (XManController.Hour in 18..20){
            do18To21()
        }
    }

    fun do18To21(){
        Log.i("tang","和XWoman约会")
    }
}

现在我们已经做到这里了,就只差最后一步了。

步骤五:调用状态模式。(此处我是在Activity中调用)

class XManActivity :AppCompatActivity(),View.OnClickListener{

    private lateinit var btn_xman_start:Button
    //创建控制器对象
    private var XManControll=XManStateController()
    //当前的时间
    private var currentTime:Int=7 

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.layout_xman)

        btn_xman_start=findViewById(R.id.btn_xman_start)
        btn_xman_start.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when(v?.id){
            R.id.btn_xman_start->{
                //设置当前时间
                XManControll.Hour=currentTime
                //调用状态方法
                XManControll.XManTimeState()
            }
        }
    }
}

到此为止,整个过程已经完毕了。

我们可以看到,Activity里关于状态的控制,我们仅仅用了两行代码就实现了,整个过程逻辑清晰,代码层层分明,没有大篇幅的if else,switch来做判断。代码的可读性和解耦性都很高。

 

三.状态模式的好处

       将与特定状态相关的行为局部化,并且将不同状态的行为分割开来,消除庞大的条件分支语句,状态模式通过把各种状态转移逻辑分布到Sate的子类之间,来减少彼此之间的依赖,此时就变得容易维护和扩展了。

     (大家多使用几次也就知道状态模式的好处了,肉眼可见的好处)

 

四.状态模式的缺点

       俗话说,人无完人。这句话同理也适用于咱们的状态模式。其实你如果跟着敲,可能已经能够深切的感受到它的缺点了。

几个状态,就有几个类来处理。是不是类有点多了?我暂时发现的就是这个缺点,类过多。大家请根据自己的实际情况来酌情使用。

 

五.状态模式和策略模式的区别

     很多人如果使用过策略模式,你可能第一印象会觉得状态模式和它很像。的确,而且我看很多博主介绍的状态模式和具体实现,我发现很多人直接就把策略模式当做状态模式来使用了。这绝对是个误区!它俩无论是使用还是实现都完全不是一个东西,切忌不要觉得它俩是一样的。这也是我为什么要写这一篇博客的原因,想让大家把这两种设计模式区分开来!

说到这里我多提一句,之前我在筛选简历的时候,发现有人在简历上写 —— 熟悉设计模式MVC,MVP。

如果你也这样写的话,还是快醒醒吧。MVP和MVC,MVVM不是设计模式!这是设计架构!!!

上面多说了一写,我现在正式的介绍一下状态模式和策略模式的区别。

状态模式的类

  XManAllState:抽象状态(参数是控制器类)----------------XManStrategy:接口(参数是你要在各个策略类中要使用的变量)

  XManStateController:控制你的状态的一个控制器---------XManCal:写一个暴露的方法,该方法内调用接口的方法。

  XManActivity:调用状态的Activity-----------------------------XManActivity:把每一个策略new出来,将new出来的对象传入XManCal作为参数。随后调用XManCal暴露的方法,将变量传入方法,

  Xman7To9:状态7:00-9:00的状态类----------------------------XManStrategy1:策略1

  XMan9To12:状态9:00-12:00的状态类----------------------XManStrategy2:策略2

  XMan12To14:状态12:00-14:00的状态类-------------------XManStrategy3::策略3

看了上面的对比你可能觉得有点晕乎乎的,没关系。当你真正要使用策略模式的时候,你看一遍网上的demo也就会了。

我个人觉得状态模式比策略更难懂一些,策略模式相较很简单,他们两者最大的区别就是:它们的具体实现以及一个是抽象类,一个是接口。总而言之,他俩就是完全不同的两个东西。

 

至此,我对于状态模式的介绍也就差不多了。不正之处请多指教,如有疑问欢迎提出来,虽然我不一定能解答

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章