工作多年后,越发觉得代码整洁真的是太重要了!尤其是在团队开发中,写出优雅工整的代码,能让同事更乐于跟你合作。
下面,将通过命名、类、函数、测试这四个章节,使我们的代码变得整洁。
1.为什么要保持代码整洁?
不整洁的代码随着时间的增加而增加时,生产力会随之降低. 导致的结果就是:
- 代码不易扩展或扩展容易引发其他问题
- 程序崩溃
- 加班
- 增加公司成本(加人) 甚至可能造成公司倒闭 一图胜千言
1.1 所以从一开始就要保持整洁
所以在一开始就要写整洁的代码,如果有不整洁的代码就要及时的整改. 绝对不要有以后再改,以后再说的想法, 因为!
later equal never
想想是不是这个道理,你有多少以后再说、以后再改的东西都抛诸脑后了.
如果是一定要做的事情,那就趁早做!
1.2 如何写出整洁的代码?
那么现在的问题就是,怎样的代码才算是整洁的代码呢:
- 可读性要高: 代码要像散文一样优雅易读,见码知意
- 拒绝重复代码
- 满足设计模式原则
- 单一职责
- 开闭原则
- 里氏替换原则
- 依赖倒转原则
- 接口隔离原则
- 迪米特法则
- 合成复用法则
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
2.命名
好的命名可提高代码的可读性,让人见码知意, 降低理解成本,提高效率, 减少加班.
2.1 不好的命名方式
- 没有任何意义的命名方式
public interface Animal { void abc(); }
现在我们有一个动物的接口, 里面有一个方法abc(),看了让人一头雾水, 调用这个方法的人也完全不知道这个方法是干什么的,因为他的命名毫无意义
有意义的命名方式:
public interface Animal { void cry(); }
我们将方法名命名为cry(喊叫,呼喊),调用的人就知道这个方法的作用是什么了.
所以命名一定要有意义且让人见码知意.
- 命名前后不一致 这种情况体现在明明是同一行为,但是却有不同的命名,前后不一致,让人造成混淆.
public interface StudentRepository extends JpaRepository<AlertAll, String> { Student findOneById( @Param("id") String id ); List<Student> queryAllStudent( ); }
上面两个方法都是查询 xxx, 但是命名一会叫 query 一会叫 find,这种情况应该加以规范,保持一致, 修改后:
public interface StudentRepository extends JpaRepository<AlertAll, String> { Student findOneById( @Param("id") String id ); List<Student> findAll( ); }
- 命名冗余 体现在命名有很多没必要的成分在里面, 并且这些"废话"并不能帮助区分它们的区别, 例如在变量命名中添加了 Variable 这个词, 在表名中添加了 Table 这个词.所以命名中不要出现冗余的单词 , 并且提前约定好命名的规范.
// 获取单个对象的方法用get做前缀 getXxx(); //获取多个对象用list做前缀 listXxxx();
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
3.类
整洁的类应满足一下内容:
- 单一职责
- 开闭原则
- 高内聚性
3.1单一职责
类应该短小,类或模块应有且只有一条加以修改的理由 , 如果一个类过于庞大的话,那么说明它承担的职责过多了.
优点:
- 降低类的复杂度
- 提高类的可读性
- 提高系统的可维护性
- 降低变更引起的风险
如何判定类是否足够短小?
通过计算类的职责来判断是否够短小,类的名称描述其全责, 如果无法为某个类命以准确的名称, 这个类大概就太长了, 类名越含糊,可能拥有越多的职责.
职责过多的例子,可以看到以下类有两个职责:
public abstract class Sql { // 操作SQL的职责 public abstract void insert(); // 统计SQL操作的职责 public abstract void countInsert(); }
将统计的职责抽取到另一个类
public abstract class CountSql { public abstract void countInsert(); }
3.2 开闭原则
开闭原则: 面向修改关闭, 面向扩展开放.
面向修改关闭意味着增加新的逻辑不会修改原有的代码,降低了出错的可能性.
面向扩展开放则是提高了代码的可扩展性,可很容易的增加新的代码逻辑.
不满足开闭原则的例子:
public abstract class Sql { public abstract void insert(); public abstract void update(); public abstract void delete(); }
如果我们现在要新增查询的操作,就需要修改Sql这个类,没有做到面向修改关闭
重构后:
public abstract class Sql { public abstract void generate(); } public class CreateSql extends Sql { @java.lang.Override public void generate() { // 省略实现 } } public class UpdateSql extends Sql { @Override public void generate() { // 省略实现 } }
当我们要增加删除方法时可以很容易的扩展.
使用大量的短小的类看似比使用少量庞大的类增加了工作量(增加了更多的类),但是真的是这样吗? 这里有一个很好的类比:
你是想把工具归置到有许多抽屉、每个抽屉中装有定义和标记良好的组件的工具箱呢, 还是想要少数几个能随便把所有东西扔进去的抽屉?
最终的结论:
系统应该由许多短小的类而不是少量巨大的类组成,每个小类封装一个权责,只有一个修改的原因,并与少数其他类一起协同达成期望的系统行为.
3.3 内聚
方法操作的变量越多,就越粘聚到类上. 如果一个类中的每个变量都被每个方法所使用, 则该类具有最大的内聚性. 我们应该将类的内聚性保持在较高的位置. 内聚性高意味着方法和变量互相依赖, 互相结合成一个逻辑整体.
为什么要保持高内聚? 保持内聚性就会得到许多短小的类,就越满足单一职责.
内聚性低怎么办? 如果类的内聚性就不够高,就将原有的类拆分为新的类和方法.