上一篇博客说到最近做了一个大一些的需求,等需求完成后代码非常的凌乱,自己重构(整理了一波),在整理过程中,有一点对于如何优化代码的想法,特此记录一下。
这里说的优化,是指完成了杂乱的代码后,重现让它更合理,更干净一些,并不是在编程开始前的设计优化,因此不着重于设计模式等的使用。
在代码的review过程中,推荐使用一些gitlab,gerrit等工具来查看自己的代码,因为git工具会将你的代码改动更加直观的展示出来,而在编译器中,我们看到的更多是整体的代码,容易分散注意力。
代码的乱,乱在哪里,一是程序本身的属性不够好,如扩展性,健壮性等。二是可读性不够好,不能很直观的读懂代码。下面是针对这两个方面的几个小方法。
这一步其实很重要,因为好的设计可从根本上提高代码的质量,但是因为过于有“个性”,每个项目都有自己适合的设计,无法具体分析。
但是在编码完成后,可以暂时忘掉细节代码,想一下整体的项目需求,套用一下基本的设计模式,比如最简单的工厂模式,观察者模式等等,如果真的有很合适的,千万不要畏惧额外的工作量,改他!
这一步其实非常的简单,把代码中使用到的实体类,各种类的属性看一下,有没有一些重合度很高的?比如有一个手机类,一个牙刷类,他们都有一个属性叫做出厂日期和预计使用时长,如下:
public class Phone {
String model;
int useTime;
String createDate;
}
public class Toothbrush {
String size;
int useTime;
String createDate;
}
这个时候就应该考虑是否将这两个属性提取出来,作为基类的存在,比如:
public class Product {
int useTime;
String createDate;
}
public class Phone extends Product {
String model;
}
public class Toothbrush extends Product {
String size;
}
这样看上去是不是好多了?
在思想上:手机和牙刷都属于产品,产品会有出厂日期和使用时长等属性。
在代码上:这样看上去也会整洁一些。
也许有的朋友会说了,这样看起来改变不是很大呀?值得折腾一下吗?
试想一下当后续拥有1w个产品呢?2w呢?每个类都写这两个属性会多多少代码呢?
这一点是我今天主要改动的一些地方,总是编码的过程中不自觉的陷入面对过程编程,然后一溜儿的代码就写出来了,其实都不是很符合面对对象的设计。
还是上面的例子,现在我们要检查一下一个手机使用时间过长了没?来决定能否对他进行二次回收。我们在service层写了一个方法如下:
//测试,没有纠结具体实现及参数类型
//传入日期和手机,判断手机是否过期,过期则返回空,不过期则返回该手机
public Phone huishou(String date, Phone phone) {
String phoneLastDate = phone.createDate + phone.useTime;
if (date.compareTo(phoneLastDate) > 0) {
return null;
}
return phone;
}
看起来是不是特别没有毛病,我们先计算出了该手机可以被使用的最后一天,然后和传入的日期比较来决定是否回收。。。。
但是这样每次回收都需要写一遍比较的这个方法,好歹封装个方法啊(我开始的做法):
//测试,没有纠结具体实现及参数类型
//传入日期和手机,判断手机是否过期,过期则返回空,不过期则返回该手机
public Phone huishou(String date, Phone phone) {
if (isOverTime(date,phone)){
return null;
}
return phone;
}
//判断是否过期封装为函数
public boolean isOverTime(String date, Phone phone) {
String phoneLastDate = phone.createDate + phone.useTime;
return date.compareTo(phoneLastDate) > 0;
}
这样子将判断的过程封装为函数,每次回收的时候调用一下这个函数,来决定是否回收就好了。这样多个后手函数也只用这一个函数,省了好多代码!
我真机智!!!!
其实还可以把这个函数写成泛型,凡是继承Product的类都可以判断,又省了一波代码,但是这不是这里的重点,不再展开了。。。
我开始就走到了封装这一步,觉得自己很机智,后来一想,这个方法应该存在于这里吗?手机有出厂日期和使用时间长度的属性,就应该有是否过期的参数或者方法来告诉使用手机的人(或者对象)呀,这个是不是放在手机自身,甚至放在Product基类里面更合适呢?
像这样:
/基类
public class Product {
int useTime;
String createDate;
public boolean isOverTime(String date) {
String productLastDate = createDate + useTime;
return date.compareTo(productLastDate) > 0;
}
}
判断是否回收变成了这样:
//测试,没有纠结具体实现及参数类型
//传入日期和手机,判断手机是否过期,过期则返回空,不过期则返回该手机
public Phone huishou(String date, Phone phone) {
if (phone.isOverTime(date)) {
return null;
}
return phone;
}
这时候的思想是:拿到手机和日期,问手机:你过期了没!!按照手机的回答决定要不要抛弃(回收)他。
这一步其实涉及到面对对象的设计,其实说起面对对象,我们很多人都可以头头是道,封装,集成,balabala,但是在实际的编码过程中?我们真的有遵守面对对象的思想吗?我们的代码真的可以称得上是面对对象吗?
我不确定,我今天才发现这一点,那我以往的代码应该有很多犯了类似的错误了。
虽然我们在编码过程中已经注意变量的定义,但是毕竟当时心系代码,总有疏漏,所以检查一遍总是没错的!
int color = 2;
应该定义为:
public static final int COLOR_RED = 2;
int color = COLOR_RED;
这样方便后续修改,也方便其他程序员阅读。
当你回首看自己的代码,如果有任何地方稍有困惑,而且不修改的话,那就意味着这里需要注释,因为你自己写的都会困惑,其他人应该就蒙了.
如果你的代码足够好,有自解释能力,那么是不需要添加注释的,但是对于普通人来说,我们还是应该在名称不够好的变量,方法上添加注释。
尤其是对外提供的接口以及协议文件如proto里,注释尽可能详细些,否则你会不断地被联调接口的人打扰!
暂时就想到这么多啦!后续有合适的再进行添加。
完。
2018-09-29 完成
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
联系邮箱:huyanshi2580@gmail.com