为了减少代码复杂度,我将if-else升级为面向状态编程
摘要:面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。
本文分享自华为云社区《从面向if-else编程升级为面向状态编程,减少代码复杂度》,作者:breakDraw。
面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上。
需求
有一个非常经典的数字校验场景, 需求如下:

复杂度高的硬写代码
这时候如果直接硬写,大概率写出容易复杂度巨高的代码,还容易遗漏而出错。
例子如下:
class Solution {
public boolean isNumber(String s) {
int sign = 1;
int pointSign = 1;
int eSign = 1;
int numSign = -1;
int i = 0;
int n = s.length();
while(i<n){
if(s.charAt(i)>='0'&&s.charAt(i)<='9'){
numSign = 1;
sign = -1;
}else if(s.charAt(i)=='+'||s.charAt(i)=='-'){
if(sign>0){
sign = -sign;
}else{
return false;
}
if(i>0&&s.charAt(i-1)=='.'){
return false;
}
}else if(s.charAt(i)=='.'){
//numSign = -1;
if(pointSign>0){
pointSign = -pointSign;
}else{
return false;
}
if(i>0&&(s.charAt(i-1)=='e'||s.charAt(i-1)=='E')){
return false;
}
}else if(s.charAt(i)=='e'||s.charAt(i)=='E'){
if(eSign<0||numSign<0){
return false;
}
eSign = -1;
sign = 1;
numSign = -1;
pointSign = -1;
}else{
return false;
}
i++;
}
return numSign>0;
}
}
这段代码的复杂度为 21, 放在科目一考试直接不及格了,而且非常容易出错,改着改着把自己改晕了,或者改漏了。

§ 状态机优化
图片引用自Leetcode官方题解,链接见:
https://leetcode-cn.com/problems/valid-number/solution/you-xiao-shu-zi-by-leetcode-solution-298l/

可以看到校验的过程可以组成一个状态, 当遇到特定字符时,进入特定的状态去判断,并且该状态后面只能接入有限的状态。因此我们可以定义N个状态,每个状态定义X个状态变化条件和变化状态。
在java中用多个map即可进行维护这种关系。
可以写出如下的代码, 虽然代码量看起来更高了,但是可维护性和复杂度变强不少。
class Solution {
public enum CharType {
NUMBER,
OP,
POINT,
E;
public static CharType toCharType(Character c) {
if (Character.isDigit(c)) {
return NUMBER;
} else if (c == '+' || c == '-') {
return OP;
} else if (c == '.') {
return POINT;
} else if (c =='e' || c == 'E') {
return E;
} else {
return null;
}
}
}
public enum State {
INIT(false),
OP1(false),
// 在.前面的数字
BEFORE_POINT_NUMBER(true),
// 前面没数字的点
NO_BEFORE_NUMBER_POINT(false),
// 前面有数字的点
BEFORE_NUMBER_POINT(true),
// 点后面的数字
AFTER_POINT_NUMBER(true),
// e/E
OPE(false),
// E后面的符号
OP2(false),
// e后面的数字
AFTER_E_NUMBER(true);
// 是否可在这个状态结束
private boolean canEnd;
State(boolean canEnd) {
this.canEnd = canEnd;
}
public boolean isCanEnd() {
return canEnd;
}
}
public Map<State, Map<CharType, State>> transferMap = new HashMap<>() {{
Map<CharType, State> map = new HashMap<>() {{
put(CharType.OP, State.OP1);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
put(CharType.POINT, State.NO_BEFORE_NUMBER_POINT);
}};
put(State.INIT, map);
map = new HashMap<>() {{
put(CharType.POINT, State.NO_BEFORE_NUMBER_POINT);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
}};
put(State.OP1, map);
map = new HashMap<>() {{
put(CharType.POINT, State.BEFORE_NUMBER_POINT);
put(CharType.NUMBER, State.BEFORE_POINT_NUMBER);
put(CharType.E, State.OPE);
}};
put(State.BEFORE_POINT_NUMBER, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
}};
put(State.NO_BEFORE_NUMBER_POINT, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
put(CharType.E, State.OPE);
}};
put(State.BEFORE_NUMBER_POINT, map);
map = new HashMap<>() {{
put(CharType.E, State.OPE);
put(CharType.NUMBER, State.AFTER_POINT_NUMBER);
}};
put(State.AFTER_POINT_NUMBER, map);
map = new HashMap<>() {{
put(CharType.OP, State.OP2);
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.OPE, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.OP2, map);
map = new HashMap<>() {{
put(CharType.NUMBER, State.AFTER_E_NUMBER);
}};
put(State.AFTER_E_NUMBER, map);
}};
public boolean isNumber(String s) {
State state = State.INIT;
for (char c : s.toCharArray()) {
Map<CharType, State> transMap = transferMap.get(state);
CharType charType = CharType.toCharType(c);
if (charType == null) {
return false;
}
if (!transMap.containsKey(charType)) {
return false;
}
// 状态变更
state = transMap.get(charType);
}
return state.canEnd;
}
}
可以看到复杂度也只有8,不会复杂度超标。

为了减少代码复杂度,我将if-else升级为面向状态编程的更多相关文章
- 提升代码幸福度,五个技巧减少js开发中的if else语句
壹 ❀ 引 在JavaScript开发中,条件判断语句的使用频率是极高的,而对于条件判断简单易读的if else应该都是大家的首选.可是代码写的久了,我们总是希望自己的代码看着能更为简洁规范(逼格更 ...
- Scala减少代码重复
高阶函数可以把其它函数当作函数参数,帮助我们减少代码重复,例如: object FileMatcher { private def fileHere = (new File(".\\file ...
- 前端程序员的蜕变——JS的 event 对象属性、使用实例、兼容性处理(极大提高代码效率、减少代码量)
下面讨论一下 js 中的 Event 对象,主要从以下三个方面详细的描述(点击标题可跳转到对应部分): 1.什么是event 2.怎么用event,用他该注意什么,几个简单实际应用 3.event在不 ...
- OpenCV:Mat元素访问方法、性能、代码复杂度以及安全性分析
欢迎转载,尊重原创,所以转载请注明出处: http://blog.csdn.net/bendanban/article/details/30527785 本文讲述了OpenCV中几种访问矩阵元素的方法 ...
- 使用python对py文件程序代码复用度检查
#!/user/bin/env python # @Time :2018/6/5 14:58 # @Author :PGIDYSQ #@File :PyCheck.py from os.path im ...
- VS 提升代码辨识度 (工欲善其事必先利其器)新手开发必备!
VS简化编译.提高生产 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享.心创 ...
- VS、ReSharper 设置修改代码颜色、提高代码辨识度!附VS超实用快捷!
ReSharper 配置代码颜色 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心 ...
- 【codenet】代码相似度计算框架调研 -- 把内容与形式分开
首发于我的gitpages博客 https://helenawang.github.io/2018/10/10/代码相似度计算框架调研 代码相似度计算框架调研 研究现状 代码相似度计算是一个已有40年 ...
- 相比xib 使用代码编排view 的一个明显的好处就是可以更好地重复使用已有代码,减少代码冗余。
相比xib 使用代码编排view 的一个明显的好处就是可以更好地重复使用已有代码,减少代码冗余.
- WPF INotifyPropertyChanged 通过特性减少代码量
在很多地方需要用上INotifyPropertyChanged的接口,MVVM模式,List等集合都会用到. 通常我们使用 protected void OnChange(PropertyChange ...
随机推荐
- Chromium CC渲染层工作流详解
1. Chromium 的渲染流水线 Blink -> Paint -> Commit -> (Tiling ->) Raster -> Activate -> D ...
- [C++]线段树 区间查询 单点修改
线段树 区间查询 单点修改 算法思想 这个算法是用于数组的查询和修改 可以高效的进行查询修改 但是会增加内存的使用 本质上是一种 空间换时间 的算法 这个算法把一串数组无限二分 直到分的只剩下一个数据 ...
- Vue 2.x源码学习:render方法、模板解析和依赖收集
内容乃本人学习Vue2源码的一点笔记,若有错误还望指正. 源码版本: vue: 2.6 vue-loader: 13.x vue-template-compiler: 2.6 之前的相关学习笔记: 应 ...
- 查看API官方文档
盛年不重来,一日难再晨.及时宜自勉,岁月不待人. 学习一门语言, 了解其开发文档必不可少.开发文档就是我们在编程开发.维护.升级过程中的参考最全面.最权威的资料.我认为就是开发者为后人方便理解留下 ...
- HarmonyOS 开发入门(二)
HarmonyOS 开发入门(二) 日常逼逼叨 在HarmonyOS 开发入门(一)中我们描述了 HarmonyOS 开发的语言ArKTs以及Ts简单的入门级语法操作,接下来我们进入第二部分Harmo ...
- 跨境 ERP 积加系统与金蝶云星空系统数据集成对接方案
方案简介 由于积加ERP 系统专注服务于亚马逊跨境电商是非常复杂和庞大的应用系统,具有非常丰富的业务流程.复杂的系统架构和服务接口.主要涉及系统解决店铺运营,店铺管理,供应链管理,协同智能补货.采用& ...
- mysql可视化工具有哪些?优点是什么?
MySQL 是一种广泛使用的关系型数据库管理系统(RDBMS),由于其开放源代码和高度可定制化的优势,广受开发者欢迎.为了更加高效地管理 MySQL 数据库,我们通常需要使用 MySQL 可视化工具. ...
- Centos 7在线安装JDK1.8+Tomcat+MySQL8.0+Nginx
一.安装JDK 注: 以下命令环境在Xshell中进行. 温馨提示: 在线安装方式需要有网速的前提,有的软件下载需要大量时间. 1.查询出系统自带的OpenJDK及版本 rpm -qa | grep ...
- [ABC282E] Choose Two and Eat One
Problem Statement A box contains $N$ balls, each with an integer between $1$ and $M-1$ written on it ...
- 解密视频魔法:将ExternalOES纹理转化为TEXTURE_2D纹理
在使用OpenGL ES进行图形图像开发时,我们常使用GL_TEXTURE_2D纹理类型,它提供了对标准2D图像的处理能力.这种纹理类型适用于大多数场景,可以用于展示静态贴图.渲染2D图形和进行图像处 ...