定义
允许一个对象在其内部状态改变时改变他的行为,对象看起来似乎修改了它的类。
例子:图片上传
1)常规处理
<template>
<div>
<plugin ref="plugin"></plugin>
<span>文件名称1:{{fileName}}</span>
<button data-action="button1" @click="button1Click">{{button1Text}}</button>
<button data-action="button2" @click="button2Click">删除中</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import plugin from '@/components/plugin.vue'
export default defineComponent({
components: {
plugin
},
data() {
return {
fileName: '',
state: 'sign',
button1Text: '扫描中',
delFlag: false,
}
},
created() {
this.fileName = '文件名';
window.externalUpload = (state: string) => this.changeState(state);
setTimeout(() => {
window.externalUpload('uploading')
}, 1000);
setTimeout(() => {
window.externalUpload('done')
}, 5000);
},
methods: {
button1Click() {
if (this.state === 'sign') {
console.log('扫描中,点击无效')
} else if (this.state === 'uploading') {
this.changeState('pause')
} else if (this.state === 'pause') {
this.changeState('uploading')
} else if (this.state === 'done') {
console.log('文件上传成功,点击无效')
} else if (this.state === 'error') {
console.log('文件上传失败,点击无效')
}
},
button2Click() {
if (this.state === 'done' || this.state === 'error' || this.state === 'pause') {
this.changeState('del')
} else if (this.state === 'sign') {
console.log('文件正在扫描中,不能删除')
} else if (this.state === 'pause') {
console.log('文件正在上传中,不能删除')
}
},
changeState(state: string) {
// console.log(this.$refs.plugin.done)
switch (state) {
case 'sign':
this.$refs.plugin.sign();
this.button1Text = '扫描中,任何操作无效';
break;
case 'uploading':
this.$refs.plugin.uploading();
this.button1Text = '正在上传,点击暂停';
break;
case 'pause':
this.$refs.plugin.pause();
this.button1Text = '已暂停,点击继续上传';
break;
case 'done':
this.$refs.plugin.done();
this.button1Text = '上传完成';
break;
case 'error':
this.button1Text = '上传失败';
break;
case 'del':
this.$refs.plugin.del();
console.log('删除完成')
break;
}
this.state = state;
}
}
})
</script>
2)状态模式封装
<template>
<div>
<plugin ref="plugin"></plugin>
<span>文件名称:{{fileName}}</span>
<button data-action="button1" @click="button1Click">{{button1Text}}</button>
<button data-action="button2" @click="button2Click">删除中</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import plugin from '@/components/plugin.vue'
import { UploadingState, SignState, PauseState, DoneState, ErrorState } from '@/utils/stateFac'
export default defineComponent({
components: {
plugin
},
data() {
return {
fileName: '',
state: 'sign',
currState: '',
button1Text: '',
uploadingState: new UploadingState(this),
signState: new SignState(this),
pauseState: new PauseState(this),
doneState: new DoneState(this),
errorState: new ErrorState(this),
}
},
created() {
this.fileName = '文件名';
window.externalUpload = (state: string) => this['changeState'](state);
},
mounted() {
this.$nextTick(() => {
window.externalUpload('sign')
})
setTimeout(() => {
window.externalUpload('uploading')
}, 1000);
setTimeout(() => {
window.externalUpload('done')
}, 5000);
},
methods: {
button1Click() {
this.currState.clickHandler1();
},
button2Click() {
this.currState.clickHandler2();
},
changeState(state: string) {
this.$refs.plugin[state]();
this.currState = this[`${state}State`];
const obj = {
sign: '扫描中,任何操作无效',
uploading: '正在上传,点击暂停',
pause: '已暂停,点击继续上传',
done: '上传完成',
error: '上传失败',
}
if (obj[state]) this.button1Text = obj[state];
},
}
})
</script>
// stateFac.js
export class State {
clickHandler1(){console.log('clickHandler1')}
clickHandler2(){console.log('clickHandler2')}
}
export class StateFactory extends State {
constructor(param) {
super(param)
this.uploadObj = param;
}
}
export class SignState extends StateFactory {
constructor(param) {
super(param)
}
clickHandler1(){console.log('扫描中,点击无效')}
clickHandler2(){console.log('文件正在上传中,不能删除')}
}
export class UploadingState extends StateFactory {
constructor(param) {
super(param)
}
clickHandler1(){this.uploadObj.changeState('pause')}
clickHandler2(){console.log('文件正在上传中,不能删除')}
}
export class PauseState extends StateFactory {
constructor(param) {
super(param)
}
clickHandler1(){this.uploadObj.changeState('uploading')}
clickHandler2(){this.uploadObj.changeState('del')}
}
export class DoneState extends StateFactory {
constructor(param) {
super(param)
}
clickHandler1(){console.log('文件已完成上传,点击无效')}
clickHandler2(){this.uploadObj.changeState('del')}
}
export class ErrorState extends StateFactory {
clickHandler1(){console.log('文件上传失败,点击无效')}
clickHandler2(){this.uploadObj.changeState('del')}
}
优点
- 状态模式定义了状态与行为之间的关系,并将他们封装在一个类里,通过新增的状态类,很容易增加新的状态和转换。
- 避免context无限膨胀,状态切换的逻辑被分布在状态中,也去掉了context中原本过多的条件分支。
- 用对象代替字符串来记录当前状态,使状态的切换一目了然。
- context的请求动作和状态类中封装的行为可以容易的独立变化而互不影响。
缺点
- 编写状态类会让系统增加不少对象。
- 逻辑分散在状态类中,无法在一个地方就看出现整个状态机的逻辑。
应用场景
操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态,那么可以使用状态模式来将分支的处理分散到单独的状态类中;
对象的行为随着状态的改变而改变,那么可以考虑状态模式,来把状态和行为分离,虽然分离了,但是状态和行为是对应的,再通过改变状态调用状态对应的行为;
- 下拉菜单在hover动作下有显示、悬浮、隐藏等状态。
- 一次TCP请求有建立连接、监听、关闭等状态。
- 游戏任务有攻击、防御、跳跃、跌倒等状态。