状态模式-优雅的去除if/else

本文通过蓝牙锁指令发送与响应的实例,详细介绍了从简单boolean值到状态模式的演进过程,展示了状态模式如何简化代码并提高扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:用最简单的示例搞懂状态模式,请耐心看完…


现在有这么一个需求:
app要通过蓝牙给锁发送两条指令A和B,A和B都是我简化的说法,在执行A.B之前是会有其他的前置指令需要执行,比如E-R-A,Y-R-B,那么这两条指令首先都会经过一个R的响应,那么同样的响应如何区分是A指令的响应还是B指令的响应的呢?
1.0版本来了…
我的做法是通过一个boolean值来区分 伪代码如下:

boolean isA;
public void sendA(){
	...发送的逻辑
	isA = true;
}
public void sendB(){
...发送的逻辑
	isA = false;
}

public void onResponse(){
	if(isA){
		...执行A
	}else{
		...执行B
	}
}

很顺利的解决了问题…在指令执行前通过boolean值记录当前是哪条指令 在响应时做对应的判断


2.0版本来了…
现在的需求是增加两条指令C和D,同样他们都会经过R的响应那么上述的代码就需要改动了
我的做法是改动Boolean值为Int值

int type= -1;
int A = 1;
int B = 2;
int C = 3;
int D = 4;

public void sendA(){
...发送的逻辑
	type = A;
}
public void sendB(){
...发送的逻辑
	type = B;
}
public void sendB(){
...发送的逻辑
	type = C;
}

public void sendB(){
...发送的逻辑
	type =D;
}


public void onResponse(){
	if(type == A){
		...执行A
	}else if(type == B){
		...执行B
	}else if(type == C){
		...执行C
	}else if(type == D){
		...执行D
	}
}

问题解决了…但是自1.0开始我就觉得这种代码写出来不是我想要的,当业务逻辑复杂且多的情况下,整个类中充斥着大量的boolean值 和if else判断等,当搬砖的自己都觉得写出来的代码很烂的时候,应该就是要面临优化了…


重头戏来了,最近在看(Android源码设计模式解析与实战)一书时,看到状态模式时,顿时虎躯一震


3.0版本来了…在1.0的基础上进行优化尽量减少伪代码让大家看到更清晰

interface LockState{
	void parse();
}

class A implements LockState{
	void parse(){
		...执行A
	}
}

class B implements LockState{
	void parse(){
		...执行B
	}
}

public void sendA(LockState lockState){
	this.lockState = lockState;
	...发送的逻辑
}
public void sendB(LockState lockState){
	this.lockState = lockState;
	...发送的逻辑
}
public void onResponse(){
	lockState.parse();
}

问题解决了 当我发送A指令时 sendA(new A());发送B指令时 sendB(new B());而对应的A类和B类实现具体的业务.
这在最大程度上遵循了面向对象的六大原则
对于修改:是不需要做任何改动的
对于扩展:假设现在有E指令要添加只需要创建一个E类实现LockState就可以了
当然这只是我遇到的一个简单的示例,可以通过状态模式很优雅的解决(用策略模式同)…


套用Android源码设计模式解析与实战一书中对该模式的使用场景定义:

(1) 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
(2) 代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句 if-else或switch-case ),
且这些分支依赖于该对象的状态。状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象而独立变化,这样通过多态来去除过多的、重复的if-else等分支语句。


—写在最后的啰嗦—
这应该是2018年的最后一篇博文了…
过去的时间总觉得过的很快,未来的时间又总觉得还很长

在这里插入图片描述

<template> <div style="height: 100%;width: 100%;background-color: white"> <el-row style="padding: 15px 10px;background-color: rgba(244, 244, 245,.3)"> <!-- 搜索区域 --> <div style="width: 85%;text-align: left"> <el-input v-model="searchKey" style="width:50%" placeholder="输入关键词搜索" class="input-with-select" @keyup.enter="searchData" > <template #prepend> <el-select v-model="select" placeholder="请先选择档案" style="width: 200px" @change="selectChange"> <el-option v-for="(item,index) in options" :label="item.label" :value="item.value"/> </el-select> </template> <template #append> <el-button :icon="Search" @click="searchData"/> </template> </el-input> </div> <div style="width: 15%;text-align: right;"> <el-button plain type="primary" :disabled="select ===''" @click="openInputDialog = true"> <el-text>{{ select }}</el-text>  数据导入 </el-button> </div> </el-row> <!-- 数据展示 --> <el-row style="padding: 5px 15px;height: 89%;"> <el-table v-if="select !== ''" :data="tableData" v-loading="loading" :header-cell-style="{ background: '#FFFFFF' }" style="width: 100%;height: 100%" border stripe> <el-table-column fixed type="index" width="60" label="序号"></el-table-column> <el-table-column v-for="(col,index) in computedColumns" :fixed="['projectCode', 'projectName'].includes(col.prop)" :prop="col.prop" :label="col.label" :width="col.width"> <template #default="scope" v-if="col.dataType !== ''"> <el-text v-if="col.dataType === 'fee'">{{ method.NumberThousandths(scope.row[col.prop]) }}</el-text> <el-text v-else-if="col.dataType === 'date'">{{ method.DateFormat(scope.row[col.prop]) }}</el-text> <el-text v-else></el-text> </template> </el-table-column> </el-table> </el-row> <!-- 分页区 --> <el-row> <div style="width:100%;display: flex; justify-content: center"> <el-pagination v-if="select !== ''" style="margin: 0;" v-model:current-page="pageInfo.pageNum" @current-change="currentPageChange" v-model:page-size="pageInfo.pageSize" @size-change="pageSizeChange" pager-count="7" :page-sizes="[10, 20, 50, 200]" :layout=pageLayout :total="pageInfo.total"/> </div> </el-row> </div> <!-- 数据导入窗口 --> <el-dialog v-model="openInputDialog" width="90vw" style="height: 90vh;margin: 5vh 5vw"> <el-row> <el-col :span="8" style="text-align: left"> <el-upload ref="upload" v-loading="uploading" accept=".xlsx" :limit="1" :on-exceed="handleExceed" action="" :http-request="excelResolve" :before-upload="beforeAvatarUpload" :on-success="handleAvatarSuccess" > <el-button :disabled="uploading"> {{ uploading ? '解析中...' : '📁打开文件' }} </el-button> </el-upload> </el-col> <el-col :span="8"> <el-text size="large" type="info">{{ select }}( <el-text size="small" type="danger" v-show="inputData.length !== 0">已解析:{{ inputData.length }}</el-text> ) </el-text> </el-col> <el-col :span="8" style="text-align: right"> <el-button type="primary" @click="uploadFile"> <el-icon> <UploadFilled/> </el-icon> 上传数据 </el-button> </el-col> </el-row> <el-row> <el-table :data="inputData" :virtual-scroll="inputData.length > 100" v-loading="loading" :header-cell-style="{ background: '#FFFFFF' }" style="width: 100%;height: 73.5vh" border stripe> <el-table-column fixed type="index" width="60" label="序号"></el-table-column> <el-table-column v-for="(col,index) in computedColumns" :fixed="['projectCode', 'projectName'].includes(col.prop)" :prop="col.prop" :label="col.label" :width="col.width"> <template #default="scope" v-if="col.dataType !== ''"> <el-text v-if="col.dataType === 'fee'">{{ method.NumberThousandths(scope.row[col.prop]) }}</el-text> <el-text v-else-if="col.dataType === 'date'">{{ method.DateFormat(scope.row[col.prop]) }}</el-text> <el-text v-else></el-text> </template> </el-table-column> </el-table> </el-row> <el-row> <el-text v-if="errorMessage !==''" size="default" type="danger">{{ errorMessage }}</el-text> </el-row> </el-dialog> </template> <script setup> import {computed, reactive, ref, watch} from "vue"; import {Search} from "@element-plus/icons-vue"; import apiWorkHours from "@/api/workHours/apiWorkHours"; import qs from "qs"; import {ElMessage, genFileId} from "element-plus"; import method from "../../../util/js/method"; // region 属性 let tableColumns = [ { prop: "departmentCode", label: "部门编码", width: "300", belongTo: ["部门人工标准", "部门人工工时"], dataType: '' }, { prop: "departmentName", label: "部门名称", width: "", belongTo: ["部门人工标准", "部门人工工时"], dataType: '' }, { prop: "monthPeriod", label: "月份", width: "400", belongTo: ["部门人工标准", "部门人工工时"], dataType: '' }, { prop: "laborHours", label: "工时", width: "400", belongTo: ["部门人工工时"], dataType: '' }, { prop: "laborStandard", label: "人工标准", width: "150", belongTo: ["部门人工标准"], dataType: '' }, { prop: "projectCode", label: "项目编码", width: "200", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "projectName", label: "项目名称", width: "400", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "laborCost", label: "人工费", width: "150", belongTo: ["项目人工分摊"], dataType: '' }, { prop: "approvalStatus", label: "立项状态", width: "150", belongTo: ["项目档案底表"], dataType: '' }, { prop: "projectStatus", label: "项目状态", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "projectNature", label: "项目性质", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "projectCategory", label: "项目类别", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "productCategory", label: "产品类别", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "responsibleDepartment", label: "主管部门", width: "200", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "projectManager", label: "项目主管", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "financeContact", label: "财务对接人", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: '' }, { prop: "projectLeader", label: "项目负责人", width: "150", belongTo: ["项目档案底表"], dataType: '' }, { prop: "projectStartDate", label: "项目开始时间", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: 'date' }, { prop: "expectedEndDate", label: "项目结束时间", width: "150", belongTo: ["项目人工分摊", "项目档案底表"], dataType: 'date' }, { prop: "currency", label: "单位", width: "80", belongTo: ["项目档案底表"], dataType: '' } ] //table列配置 let errorMessage = ref('') const upload = ref() const loading = ref(false) // 数据查询loading const uploading = ref(false) // 数据解析loading const openInputDialog = ref(false) // 数据导入窗口 let tableData = ref([]) // 查询数据 let inputData = ref([]) // excel解析的数据 const pageInfo = reactive({ pageNum: 1, pageSize: 10, total: 0 }) // 分页器 // endregion // region API const getWhDepartmentLaborPageApi = () => {// 部门档案page try { const param = { pageNum: pageInfo.pageNum, pageSize: pageInfo.pageSize } apiWorkHours.getWhDepartmentLaborPage(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list; pageInfo.total = res.data.total } else { ElMessage.error('部门档案信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const getWhDepartmentLaborPageBySearchApi = () => {// 部门档案page try { const param = { searchKey: searchKey.value, pageNum: searchKey.value === '' ? 1 : null, pageSize: searchKey.value === '' ? 10 : null, } apiWorkHours.getWhDepartmentLaborPageBySearch(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list; pageInfo.total = res.data.total ElMessage.success('共查询到:' + pageInfo.total + " 条记录") } else { ElMessage.error('部门档案信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const laborHoursUpdateBatchAPI = () => { const param = inputData.value apiWorkHours.laborHoursUpdateBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.error("成功更新:" + res.data + "条") } else { ElMessage.error("更新失败") } }) } const laborStandardUpdateBatch = () => { const param = inputData.value apiWorkHours.laborStandardUpdateBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.error("成功更新:" + res.data + "条") } else { ElMessage.error("更新失败") } }) } const laborStandardInsertBatch = () => { const param = inputData.value apiWorkHours.laborStandardInsertBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.success(res.data) } else { ElMessage.error("插入失败") } }) } const laborHoursInsertBatch = () => { const param = inputData.value apiWorkHours.laborHoursInsertBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.success(res.data) } else { ElMessage.error("插入失败") } }) } const projectInfoInsertBatch = () => { const param = inputData.value apiWorkHours.projectInfoInsertBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.success(res.data) } else { ElMessage.error("插入失败") } }) } const laborShareInsertBatch = () => { const param = inputData.value apiWorkHours.laborShareInsertBatch(param).then(res => { console.log(res) if (res.code === 1000) { ElMessage.success(res.data) } else { ElMessage.error("插入失败") } }) } const getWhLaborSharePageApi = () => {// 人工分摊page try { const param = { pageNum: pageInfo.pageNum, pageSize: pageInfo.pageSize } apiWorkHours.getWhLaborSharePage(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list; pageInfo.total = res.data.total } else { ElMessage.error('人工分摊信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const getWhLaborSharePageBySearchApi = () => {// 人工分摊page try { const param = { searchKey: searchKey.value, pageNum: searchKey.value === '' ? 1 : null, pageSize: searchKey.value === '' ? 10 : null, } apiWorkHours.getWhLaborSharePageBySearch(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list; pageInfo.total = res.data.total ElMessage.success('共查询到:' + pageInfo.total + " 条记录") } else { ElMessage.error('人工分摊信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const getProjectInfoPageApi = () => {// 项目档案page try { const param = { pageNum: pageInfo.pageNum, pageSize: pageInfo.pageSize } apiWorkHours.getProjectInfoPage(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list; pageInfo.total = res.data.total } else { ElMessage.error('项目档案信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const getProjectInfoPageBySearchApi = () => {// 项目档案page try { const param = { searchKey: searchKey.value, pageNum: searchKey.value === '' ? 1 : null, pageSize: searchKey.value === '' ? 10 : null, } apiWorkHours.getProjectInfoPageBySearch(qs.stringify(param)).then(res => { if (res.code === 1000) { tableData.value = res.data.list pageInfo.total = res.data.total ElMessage.success('共查询到:' + pageInfo.total + " 条记录") } else { ElMessage.error('项目档案信息查询失败!') } loading.value = false }) } catch (e) { ElMessage.error('程序错误:', e) } } const excelResolveWhDepartmentLaborAPI = (file) => { const param = { excelFile: file } apiWorkHours.excelResolveWhDepartmentLabor(param).then(res => { if (res.code === 1000) { inputData.value = res.data uploading.value = false ElMessage.success("数据解析完成") } else { ElMessage.error("数据解析失败") errorMessage.value = res.data } }) } const excelResolveWhLaborShareAPI = (file) => { const param = { excelFile: file } apiWorkHours.excelResolveWhLaborShare(param).then(res => { if (res.code === 1000) { inputData.value = res.data uploading.value = false ElMessage.success("数据解析完成") } else { ElMessage.error("数据解析失败") errorMessage.value = res.data } }) } const excelResolveWhProjectInfoAPI = (file) => { const param = { excelFile: file } apiWorkHours.excelResolveWhProjectInfo(param).then(res => { if (res.code === 1000) { inputData.value = res.data uploading.value = false ElMessage.success("数据解析完成") } else { ElMessage.error("数据解析失败") errorMessage.value = res.data } }) } // endregion // region 搜索栏 const select = ref('') const options = [ { value: '部门人工标准', label: '部门人工标准', }, { value: '部门人工工时', label: '部门人工工时', disabled: false, }, { value: '项目人工分摊', label: '项目人工分摊', }, { value: '项目档案底表', label: '项目档案底表', }, ] const searchKey = ref('') const searchData = () => { pageLayout = searchKey.value === '' ? "total, sizes, prev, pager, next, jumper" : "total" switch (select.value) { case "部门人工标准": getWhDepartmentLaborPageBySearchApi() break; case "部门人工工时": getWhDepartmentLaborPageBySearchApi() break; case "项目人工分摊": getWhLaborSharePageBySearchApi() break; case "项目档案底表": getProjectInfoPageBySearchApi() break; default: loading.value = false ElMessage.warning('请先选择档案!') } } // endregion // region 数据展示 // 增加计算属性 const computedColumns = computed(() => { return tableColumns.filter(column => column.belongTo.includes(select.value)) }) // 选项切换→更换数据 const selectChange = (value) => { loading.value = true tableData.value = [] switch (value) { case "部门人工标准": getWhDepartmentLaborPageApi() break; case "部门人工工时": getWhDepartmentLaborPageApi() break; case "项目人工分摊": getWhLaborSharePageApi() break; case "项目档案底表": getProjectInfoPageApi() break; default: loading.value = false ElMessage.warning('请先选择档案!') } } // endregion // region 分页 let pageLayout = ref("total, sizes, prev, pager, next, jumper") const currentPageChange = () => { selectChange(select.value) } const pageSizeChange = () => { selectChange(select.value) } // endregion // region 数据导入 // 添加watch监听对话框状态,重置上传状态 watch(openInputDialog, (newVal) => { if (newVal) { upload.value?.clearFiles(); inputData.value = []; uploading.value = false; }else { setTimeout(() => selectChange(select.value), 200) } }) const beforeAvatarUpload = (rawFile) => {// 上传前钩子 if (rawFile.size / 1024 / 1024 > 5) { ElMessage.error('文件较大 已超过5MB!') return false } return true } const handleAvatarSuccess = (response, uploadFile) => {// 文件上传成功 ElMessage.success("文件上传成功:", response) // console.table("uploadFile:", uploadFile) } const handleExceed = (files) => { }; const excelResolve = (file) => {// Excel解析,根据选择的上传数据类型选择调用的解析接口 uploading.value = true switch (select.value) { case "部门人工标准": file.name = "部门人工标准-导入模板" excelResolveWhDepartmentLaborAPI(file) break; case "部门人工工时": file.name = "部门人工工时-导入模板" excelResolveWhDepartmentLaborAPI(file) break; case "项目人工分摊": excelResolveWhLaborShareAPI(file) break; case "项目档案底表": excelResolveWhProjectInfoAPI(file) break; default: uploading.value = false ElMessage.warning('请先选择档案!') } } const uploadFile = () => { switch (select.value) { case "部门人工标准": laborStandardInsertBatch() break; case "部门人工工时": laborHoursInsertBatch() break; case "项目人工分摊": laborShareInsertBatch() break; case "项目档案底表": projectInfoInsertBatch() break; default: uploading.value = false ElMessage.warning('请先选择档案!') } } // endregion </script> <style scoped lang="scss"> .input-with-select .el-input-group__prepend { background-color: var(--el-fill-color-blank); } </style> 为什么点击upload打开文件选择时会卡死,页面无响应
最新发布
07-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值