这篇文章给新入门的程序员看的,高手就不要看了,对于刚开始参加工作,或者在大学学习了计算机的编程知识,不管是学的C语言,还是JAVA,还是C# 等等,都迫切的想要提高自己,这篇文章将结合本人多年的编程经验,说一说新手在编程中,应当注意的一些事项。这些代码,可能工作了很多年的程序员还在写,有可能你也在写,有可能是你不得不这么做,看完了这篇文章,相信你对程序 员为什么写出了屎山一样的代码,就知道点原因了。这可能是一个沉重的话题,不该新手来承担,每个程序员优不要随意的写if else,那么代码就更好维护了。
你真的学会了if else 了吗?
场景1,我们有1个变量的值type:
type =0 表示A种情况 type=1 表示另外一种情况,可能会有更多的情况,在不用switch的情况下,你会有以下2种写法(java版本的,且省略了大括号,你们写代码的时候,即使只有1行代码,也要写{}):
推荐写法:
1 | if(type == 0) |
不推荐写法:
1 | if(type == 0) |
这种写法,在某一天,如果type的值增加了1个,测试的时候容易出现诡异的BUG,如果你不测试,直接上线了,那可能会更糟糕。
严格检查类型的值,否则容易出错,当type的类型是固定的几个值的时候,即使是用switch case when实现,也要注意这种类似的错误,尤其是以后typ的值增加了,错误的写法还不容易发现,尽量让代码早报错,可以避免不少BUG
核心思想:严格检查类型的值,程序要早报错
延伸 你对抽象的理解对吗?
我们接着上文,假设有变量a b,a、b在不同的情况下,有不同的业务场景,例如:
a == 1 && b== 1 表示客户现金支付
a == 1 && b == 2 表示客户信用卡支付
推荐写法:
1 | if(a == 1 && b == 1) { |
不推荐写法:
1 | if(a == 1 && b == 1) { |
这2种写法会有细微的区别,看起来功能是一样的,在逻辑简单的时候,几乎一样,当以后的逻辑更复杂的时候,会有不同,例如 增加了1个变量c,前面1种情况,可以更好的处理增加了c的这种情况, 后面的写法,就需要慎重的考虑,c应该放在哪个位置,增加了else,测试的时候就复杂了很多。
一种更好的办法是抽象逻辑
1 | int type; |
这样就能在众多复杂的代码中,把业务发生代码的前缀,和业务的实际处理逻辑,剥离开来,将代码抽象成2部分,1部分用来描述业务发生的条件,1部分用来描述业务条件触发之后的逻辑。
上述的代码,只是为了演示使用,不能作为实际的支付逻辑的处理。
如果对上述代码,再进行修改的话:
1 | if(a == 1 && b == 1) { |
修改为:
1 | if(cash(a,b)) { |
这样是不是代码更容易阅读了呢? 我们是不是可以更进一步,把type的值用枚举来表示呢?
1 | enum PAY_CHANNEL { |
当然,要不要把一个if else写到如此的复杂,就要看实际的需求的情况了。
还有一种比较容易出错的地方是,只写了if ,不写else ,这种写法,也会带来一些隐蔽的错误,当然,并不是任何情况下,不写else,都会出错,只是写代码的时候,要考虑下,到了else,我该怎么办? 所以:
1 | if( cond ) { |
结果等同于
1 | if( cond ) { |
总是要确认,是不是真的在else中什么都不做。
DEBUG 可以有一些技巧
在JAVA中,你可以定义一个测试类,叫DebugConstant,然后其中可以定义很多debug的常量信息,例如:
1 | public static boolean DEBUG = false; |
然后在业务代码中,写:
1 | if(DEBUG) |
这样在单元测试中,就可以开启DEBUG或者,你也可以设置Java的启动参数 -DDEBUG=true 来启动DEBUG信息,当然,如果你学习的是C语言,那就可以使用条件编译了,定义1个变量DEBUG,需要DEBUG的时候就开启
需要debug的时候,直接
1 | #define DEBUG 1 |
核心思想: 善用条件编译,来实现DEBUG
finally只用来释放资源
看看这段代码会返回什么?
1 | try { |
实际会返回false,所以给我们的启示就是finally中,不能返回值只可以用来做释放资源(文件流、锁等)
谨慎修改常量的值
如果你修改了1个常量的值,一定要全局搜索所有使用这个常量的地方,仔细判断这个修改,是否有副作用。需要注意的是,注解里面不可以使用常量,一定要特别注意,尤其是mybatis的多数据源配置。
思考下return的位置
在日常的开发中,经常会写1个函数,返回1个值,在函数执行的过程中,会有各种各样的判断,各种情况,可能会中途跳出,如果是C语言,可以使用goto,如果是Java的话,就需要仔细思考下,把return的位置放在哪里,接下来仔细研究,该怎么做:
提前声明返回值,并在最后返回。
这种方法是1种最常见的写法,不容易出错,而且代码容易看懂,也容易跟踪变量的变化,如果业务十分复杂,难免会增加复杂度,但是不论如何增加复杂度,
在AB之间,都不要写return就行了。
1 | public int sum(int a,int b) { |
上面的代码在稳定运行之后,需求有变化,需要判断一些特殊值,而且这个业务,可能和这个方法没有特别大的关系,当然,且不论是否应该修改sum方法,更好的办法当然是在调用sum方法的地方,去处理特殊值,这里我们假设,我们必须修改sum方法,可能是因为有很多的地方都调用了sum方法,导致如果不修改sum方法,而是去修改调用sum方法的地方,会修改很多处代码。
从现在开始,就有2种写法了:
方法1(不推荐):
1 | public int sum(int a,int b) { |
方法2(推荐):
1 | public int sum(int a,int b) { |
这2个方法是完全不同的编码习惯,相信我,在你的编程生涯中,你会无时不刻的面临这个选择,所以需要慎重的考虑下,到底应该采用哪种方法来处理这种情况,接下来,我们稍微把刚才的例子,再增加一些变化,这里我们选择使用推荐的方法2来做变化,增加了1个需求,如果a = 15,那么就固定的返回错误-1,该怎么做呢?
方法1: 在方法开头的地方直接判断并返回
1 | public int sum(int a,int b) { |
方法2: 在逻辑运算中增加1种情况
1 | public int sum(int a,int b) { |
现在,我们拥有了1个相对来说比较复杂的方法,有正常的业务逻辑,但是代码的量不多,有异常处理,有特殊业务的处理,这个方法已经比最开始的代码,复杂了不少,在实际的开发中,可能此时这个方法已经不是30行左右了,而是300行 600行了,恭喜你,你已经在制造所谓的“代码屎山”了,多年以后,当你再看到这段代码的时候,可能你也会在心里狠狠的咒骂一句,那个XX写的代码,然后看了代码的提交记录,发现是自己写的,默默的把它关闭了。也可能是某位已经离职/在职/升职的同事写的,不过无论是谁写的,这都是一个不美好的一天。
然后我们来思考以下对策,该怎么处理,这里我们仍然坚持最开始的初衷,AB之间不返回值(如果代码中途返回值会大大增加复杂度),此时需要处理的是,判断哪些是异常情况,哪些是正常情况,也就是说,对参数进行检查,是可以方法的开头的,这并不影响:
1 | if(a == 15) { |
可以在方法的开头,增加参数的有效性的检查,同时,要把各种业务情况抽象出来,走不同的分支,具体的做法,可以参考本文开头的if else的部分,总之,不能在方法的中间随意的return,但这也仅仅是一家之言,如果不同意这个看法,也可以不考虑这个准则。
结论: 尽量不在方法体中间return
全文完。