最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • javascript 设计模式之状态模式

    正文概述 掘金(webInRun)   2021-05-18   473

    概念

    从电灯说起

    需求:

    实现一个开关功能的电灯

    代码实现:

    class Light {
        constructor() {
            this.state = 'off' // 默认关灯
            this.button = null
        }
        init() {
            const button = document.createElement('button')
            this.button = document.body.appendChild(button)
            this.button.innerHTML = '开关'
            this.button.onclick = () => {
                this.handleStateChange()
            }
        }
    
        handleStateChange() {
            if(this.state == 'off') {
                console.info("开灯")
                this.state = 'on'
            } else if(this.state == 'on') {
                console.info('关灯')
                this.state = 'off'
            }
        }
    }
    
    const light = new Light()
    light.init()
    

    这个功能暂时告一段落。

    但过不久产品经理要求实现这样一种电灯:第一次按下打开弱光,第二次按下打开强光,第三次才是关闭电灯。 你只得更改 handleStateChange 代码为

    handleStateChange () {
        if (this.state === 'off') {
          console.log('弱光')
          this.state = 'weakLight'
        } else if (this.state === 'weakLight') {
          console.log('强光')
          this.state = 'strongLight'
        } else if (this.state === 'strongLight') {
          console.log('关灯')
          this.state = 'off'
        }
    }
    

    功能是实现了,但存在如下问题:

    • 违反单一功能原则,Light 类中应该只负责状态的切换,不应该包含各种状态的逻辑处理
    • 违反开放封闭原则,如果要再加个魔幻灯关类型,还得往 handleStateChange 函数里添加一段逻辑,这样提测的时候,还要求测试人员重头将所有电灯状态测遍。
    • 通过 if else 来做状态切换,如果要实现同时满足多个状态啥的,就会很难维护

    优化一(采用状态模式)

    首先先拆解出该电灯存在三种状态:OffLightState (关灯)、WeakLightState (弱光)、StrongLightState (强光)。 编写状态类如下:

    class OffLightState {
        construct (light) {
          this.light = light
        }
      
        handleStateChange () {
          console.log('弱光')
          this.light.setState(this.light.weakLightState)
        }
    }
    
    class WeakLightState {
        construct (light) {
            this.light = light
        }
    
        handleStateChange () {
            console.log('强光')
            this.light.setState(this.light.strongLightState)
        }
    }
    
    class StrongLightState {
        construct (light) {
            this.light = light
        }
    
        handleStateChange () {
            console.log('关灯')
            this.light.setState(this.light.offLightState)
        }
    }
    

    编写 Light 类: 采用状态对象的形式标识当前的开关状态,而不是之前的字符串( 'weakLight'、’strongLight‘、'off')

    class Light {
        construct () {
          this.offLightState = new OffLightState(this)
          this.weakLightState = new WeakLightState(this)
          this.strongLightState = new StrongLightState(this)
      
          this.currentState = this.offLightState // 初始化电灯状态
          this.button = null
        }
      
        init () {
          const button = document.createElement('button')
          this.button = document.body.appendChild(button)
          this.button.innerHTML = '开关'
      
          this.button.onclick = () => {
            this.currentState.handleStateChange()
          }
        }
      
        setState (newState) {
          this.currentState = newState
        }
      }
      
      const light = new Light()
      light.init()
    

    有如下优点:

    • 每种状态和它对应的行为之间的关系局部化,这些行为被分散在各个对象的状态类之中,便于阅读和管理。
    • 状态之间的切换逻辑分布在状态类内部,这使得我们无需编写if-else语句来控制状态直接的切换。
    • 当我们需要为 Light 类增加一种新的状态时,只需要增加一个新的状态类,再稍微改变一下现有的代码。

    优化二(JavaScript版本策略模式)

    非面向对象实现的策略方式,将各个状态定义在一个对象里,通过对象映射的方式,以及 call 语法绑定主体类 Light

    const lightState = {
      'offLight': {
        handleStateChange:function() {
          console.log('弱光')
          this.setState(lightState.weakLight)
        }
      },
      'weakLight': {
        handleStateChange:function() {
          console.log('强光')
          this.setState(lightState.strongLight)
        }
      },
      'strongLight': {
        handleStateChange:function() {
          console.log('关灯')
          this.setState(lightState.offLight)
        }
      }
    }
    
    class Light {
      constructor () {
        this.currentState = lightState.offLight // 初始化电灯状态
        this.button = null
      }
    
      init () {
        console.info(this,"this")
        const button = document.createElement('button')
        this.button = document.body.appendChild(button)
        this.button.innerHTML = '开关'
        this.button.onclick = () => {
          this.currentState.handleStateChange.call(this) // 通过 call 完成委托
        }
      }
    
      setState (newState) {
        this.currentState = newState
      }
    }
      
    const light = new Light()
    light.init()
    
    

    状态模式使用场景

    • 一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为。
    • 一个操作中含有大量的分支语句,而且这些分支语句依赖于该对象的状态。状态通常为一个或多个枚举常量的表示。

    采用 javascript-state-machine 完成状态模式

    比如实现个收藏,与取消收藏功能

    import StateMachine from 'javascript-state-machine'
    import $ from 'jquery'
    var fsm = new StateMachine({
    	init: '收藏',
    	transitions: [
    		{ name: 'doStore', from: '收藏', to: '取消收藏' },
    		{ name: 'deleteStore', from: '取消收藏', to: '收藏' }
    	],
    	methods: {
    		onDoStore: function () {
    			console.log('收藏成功')
    			updateText()
    		},
    		onDeleteStore: function () {
    			console.log('取消收藏成功')
    			updateText()
    		}
    	}
    })
    const updateText = function () {
    	$('#btn1').text(fsm.state)
    }
    
    $('#btn1').on('click', function () {
    	if (fsm.is('收藏')) {
    		fsm.doStore()
    	} else {
    		fsm.deleteStore()
    	}
    })
    // 初始化
    updateText()
    
    

    采用状态模式实现 promise

    promise 的 pending fulfilled rejected 是三个不同的状态,并且每种状态都有相应的行为

    import StateMachine from 'javascript-state-machine'
    var fsm = new StateMachine({
    	init: 'pending',
    	transitions: [
    		{ name: 'resolve', from: 'pending', to: 'fulfilled' },
    		{ name: 'reject', from: 'pending', to: 'rejected' }
    	],
    	methods: {
    		onResolve: function (state, data) {
    			data.successCallBacks.forEach((fn) => fn())
    		},
    		onReject: function () {
    			data.failCallBacks.forEach((fn) => fn())
    		}
    	}
    })
    class MyPromise {
    	constructor(fn) {
    		this.successCallBacks = []
    		this.failCallBacks = []
    		fn(
    			() => {
    				fsm.resolve(this)
    			},
    			() => {
    				fsm.reject(this)
    			}
    		)
    	}
    	then(successCall, failCall) {
    		this.successCallBacks.push(successCall)
    		this.failCallBacks.push(failCall)
    	}
    }
    
    const loadImg = function (src) {
    	const promise = new MyPromise((resolve, reject) => {
    		const img = document.createElement('img')
    		img.onload = function () {
    			resolve(img)
    		}
    		img.onerror = function (err) {
    			reject(err)
    		}
    		img.src = src
    	})
    	return promise
    }
    var src = ' //www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png'
    let result = loadImg(src)
    result.then(function (img) {
    	console.info('ok1')
    })
    result.then(function (img) {
    	console.info('ok2')
    })
    
    

    只是实现 promise 功能的一部分,但已足够说明状态模式的使用方式

    状态模式与策略模式的比较

    相同点:

    它们都有一个上下文、一些策略类或者状态类,上下文把请求委托给这些类来执行

    不同点:

    策略模式:各个策略类之间是平等又平行的,它们之间没有任何关系,所以客户必须熟知这些策略类的作用,以便客户自己可以随时主动切换算法。

    状态模式:状态和状态对应的行为早已被封装好,状态之间的切换也早就被规定,“改变行为”这件事发生在状态模式的内部,对于客户来说,不需要了解这些细节。比如电灯的开与关,是由程序切换的,不用用户传入状态值。

    参考链接

    JavaScript设计模式与开发实践

    结语

    你的点赞是对我最大的肯定,如果觉得有帮助,请留下你的赞赏,谢谢!!!


    起源地下载网 » javascript 设计模式之状态模式

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元