状态模式通识篇

前言

状态模式也是行为型模式中的一种,顾名思义状态模式主要是基于对象有不同的状态,从而导致具有与其对应状态的行为。

场景

为了更好的理解状态模式,我们假设有这样的需要,我们有一个电灯,电灯可以在有打开、关闭、坏的三种情况。在开灯的时候,我们可以看书;在关灯的时候,我们什么也看不到;在灯坏的时候,我们不能进行任何操作。想一想你的代码会如何写呢?

// 一如既往的零散代码 获取电灯状态
let status = light.status
switch(status){
    case 'on':console.log('light is on ,we can read books');
    break;
    case 'off':console.log('light is off ,we can not see anything');
    break;
     case 'error':console.log('light is error ,we can not do any operation');
    break;
}

但是你想一直这样写么?这个代码是只执行一次的,为了更好的进行函数的执行,我们也许会把状态判断封装为一个函数,比如下面这样。

function lightTip(status){
    case 'on':console.log('light is on ,we can read books');
    break;
    case 'off':console.log('light is off ,we can not see anything');
    break;
     case 'error':console.log('light is error ,we can not do any operation');
    break;
}
// 获取状态执行操作
let status = light.status 
lightTip(status)
// 其他逻辑里更改状态执行
light.status = 'off'
// 继续执行
let status = light.status 
lightTip(status)

思考抽象为电灯状态

等等,当这样写下去,其实明明是很规整特殊的一个电灯对象,为什么不按照灯这个类封装一下,然后根据不同状态提供行为,同时把设置状态的部分,每个状态执行操作的部分都封装一下,方便维护也方便更加规范的切换。不妨看下下面这种写法??(图是java的,js的原理是一样的,理解就好)

codepen之状态模式设计案例

// 电灯类
class light{
  setState(state){
    this.state = state
  }
  getState(){
      return this.state.status
  }
  request(){
    return this.state.handler()
  }
} 
// 状态类
class lightState{
  constructor(){
    this.status = ''
  }
  handler(context){
    console.error('不在任何有效状态')
  }
}
// 具体状态实现 启动状态
class onState extends lightState{
  constructor(){
    super();
    this.status='on'
  }
  handler(){
    console.log('light is on ,we can read books')
  }
}
// 关闭状态实现
class offState extends lightState{
  constructor(){
    super();
    this.status='off'
  }
  handler(){
    console.log('light is off ,we can not see anything')
  }
}

let lightDemo = new light()
lightDemo.setState(new onState())
lightDemo.request()

lightDemo.setState(new offState())
lightDemo.request()

这样设计区别之后,我们每个电灯都可以分别的管理,每个电灯的状态都继承自状态类,需要实现其必要的抽象方法以及在这个公共方法里执行需要的一些状态里的方法。

说好的玛丽呢

其实状态模式不一定要class类来具体实现,它更是一种设计思想,只要你是按照这样的思想去组织代码就可以了。

先说下基本的需求,我们按照游戏设定,给其提供不同的动作,包括眺,前进,射击,蹲下以及复合的一些操作,我们需要根据用户需要可以追加一系列的操作,追加之后可以执行,也可以随时追踪到玛丽最新的动作状态。

同样我会这短代码写到codepen上,方便我们看到最终设计之后的一个效果。

codepen超级玛丽案例

function MarryState(){
// 存储用户输入的状态
  var currentState = {}
  let marry = document.getElementById('marry');
  // 针对每个状态 定义对应状态需要写的代码
  var state = {
     forward:function(){
       let marginLeft = marry.style.marginLeft
       marry.style.marginLeft = !marginLeft? '15px': parseInt(marginLeft)+15+'px'
     },
    back:function(){
       let marginLeft = marry.style.marginLeft
       marry.style.marginLeft = !marginLeft? '15px': parseInt(marginLeft)-15+'px'
     },
    jump:function(){
       marry.classList.add('jump')  ;   setTimeout(function(){marry.classList.remove('jump')},1000)
     },
  }
  // 每个action的部分 提供链式操作,所以每次执行完之后返回this
  var action = {
    changeState:function(){
      var areg = arguments ;
      currentState={};
      if(areg.length){
        for(var i=0,len=areg.length;i<len;i++){
          currentState[areg[i]] = true;
        }
      }
      return this;
    },
    go:function(){
      for(var p in currentState){
        state[p] && state[p]()
      }
      return this;
    }
  }

  return {
    change:action.changeState,
    go:action.go
  }
}
// 实例化
var marrySample = new MarryState();
// 进行事件委托
let topOpt = document.getElementById("topOpt")
topOpt.addEventListener('click',function(e){
  marrySample.change(e.target.id).go()
 })
// 定义一系列的状态动作
function animateActions(){
  marrySample.change('jump','forward','forward','back').go().go().go().go()
}

animateActions()

小结

分析下,其实以上的代码完全可以通过switch分支实现,之所以封装一个对应状态对象的函数,并通过暴露改变状态的方法以及执行操作的方法执行对应状态的代码逻辑。

所以其真正适用的场景是: – 代码中包含了大量与状态相关的判断语句 – 对象的行为与状态强相关绑定,并随着状态的改变而改变 – 一个对象的状态并不是锁定的,而是可能会动态变化的,甚至是规律辩护,比如玛丽的跳跃射击的操作、向前跳跃的操作等。

这样的模式设计之后的优点是: – 减少了因为不同状态判断写的分支语句或者条件判断语句 – 每个状态维护在一个子类或者一个封装的方法里,便于单独维护 – 状态放到了内部,外部不知道状态的执行以及互相关系

这样的缺点: – 如果状态过多,会产生很多子类,第一种方式的缺点;这种时候其实可以采用封装到函数级别即可;

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏SIGAI学习与实践平台

从0到1:神经网络实现图像识别(上)

“神经网络”是“机器学习”的利器之一,常用算法在TensorFlow、MXNet计算框架上,有很好的支持。

15430
来自专栏Nicky's blog

设计模式之享元模式(结构型)

享元模式(Flyweight Pattern)就是通过共享技术实现大量细粒度对象的复用。享元模式是通过细粒度对象的共享,所以也可以说享元模式是一种轻量级模式。按...

8620
来自专栏Nicky's blog

设计模式之简单工厂模式(创建型)

简单工厂模式又称静态工厂模式。可以根据参数的不同返回不同类的实例。定义一个类来创建其它类的实例。

13210
来自专栏AI科技评论

学界 | IEEE Fellow 又增一位华人学者,中科院自动化所王亮研究员当选

AI 科技评论按:虽然 IEEE Fellow 2019 评选结果还未出炉,但雷锋网获悉,IEEE Fellow 又增一名华人入选。

20820
来自专栏机器学习算法与Python学习

闲谈 | 国内AI排名前 8 位的大学,顶起!

近日,人工智能专业作为战略新兴产业受到关注,本文整理了目前人工智能全国排名前八的大学,供家长、考生了解。

16110
来自专栏方亮

朴素、Select、Poll和Epoll网络编程模型实现和分析——模型比较

        经过之前四篇博文的介绍,可以大致清楚各种模型的编程步骤。现在我们来回顾下各种模型(转载请指明出于breaksoftware的csdn博客)

12320
来自专栏AI研习社

CVPR 2018摘要:第四部分

我们已经分三期关于CVPR 2018(计算机视觉和模式识别)会议:第一部分专门讨论计算机视觉的GAN,第二部分涉及关于识别人类(姿势估计和跟踪)的论文,第三部分...

9020
来自专栏AI科技评论

2018年度 「微软学者」获奖名单公布!11 名计算机界新力军崭露头角

AI 科技评论按:「微软学者」奖学金是微软亚洲研究院 1999 年启动的一项面向亚太地区计算机科学以及相关专业的优秀博士生的项目。该奖学金项目旨在发掘、支持和鼓...

18320
来自专栏AI研习社

CVPR 2018摘要:第二部分

今天,我们继续推出最近的CVPR(计算机视觉和模式识别)会议系列,这是世界上计算机视觉的顶级会议。 Neuromation成功参加了DeepGlobe研讨会,现...

10020

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励