让代码美观起来
发表于 2019-08-04 20:00
很早就阅读了Clean Code of Clean(英文版本的Clean Code),当时小猪站长还是个新手,正在为团队创建混乱的代码。在多年的工作中,我一再被别人的代码坑所折磨。回想起我过去留下的代码,我当然屈从于我的同事。当阅读JDK源代码或其他优秀的开源项目时,作者的代码构造良好,它们都具有相同的特征:精确的变量名、正确的设计模式、详细且不重复的注释,等等。现在重读这本书,总结内容,并加入一些你自己的见解与你分享。代码是团队沟通的方式
工作沟通,不仅仅是电子邮件或面对面的语言沟通,代码也是沟通的方式之一。用代码实现需求只是长征的第一步,代码必须表达自己的设计思想。假设您负责的功能被另一位同事接管。如果您的代码结构清晰且注释合理,您就不必频繁地询问代码问题,而不会打断您的工作。在编写代码时,您应该考虑其他人的阅读体验,减少阅读障碍,并为整个团队而不是您自己创建代码。
让营地比来时更干净
这是美国童子军的格言。美国童子军相当于半军事化管理的青年夏令营。夏令营结束后,孩子们离开了营地,打扫干净,保持干净,营地比他们来时更干净。在软件开发过程中,可以理解为不打破规则,不引入混淆。如果团队开发了一个代码规范,如类名必须有一个子系统前缀,如BiOrderService (Bi指Bi业务单元),则继续遵循;例如,该团队为公共库(如MD5)提供了加密,然后不再引入新的MD5库。在许多新手程序员开始工作之后,他们将开始看到他们不喜欢的规范。他们需要一些工具,并且不询问旧驱动程序的公共库是否直接引入熟悉的库,从而导致兼容性或其他问题。
合适的命名
一个合适的名字是最重要的,就像给新生儿取一个好名字一样重要。不恰当的命名通常是不令人满意的、误导人的、过度缩写的等等。由于英语不是我们的母语,似乎很难找到一个合适的单词命名。我的建议是,首先明确业务,组织会议,在共同的业务领域设置词语,禁止成员之间互相捏造。例如,如果您在代码中使用canteen来表示饭厅,那么就不要再创建DinnerHall了,这是啰嗦,也是一种误导。
看看反例:
// 手机号
String phone = “13421800409”;
// 获取地址
private String getDiZhi();
//修改密码
private void modifyPassword(String password1 ,String password2)
看看正例:
// 手机号 mobileNo比phone更精确
String mobileNo= “13421800409”;
// 避免英文拼音混杂
private String getAddress();
// 参数的命名要区分意义
private void modifyPassword(String oldPassowrd,String newPassword)
短小的方法
这个方法有多短还没有定论,但是一个长达500行的方法肯定会让读者抓狂。方法太长,读者不知道该往哪里看,只看到前面而忘记后面。将复杂的方法分解为简短的、逻辑简单的方法。
看看反例:
// 获取个人信息
private UserDTO getUserDTO(Integer userId)
{
//获取基本信息
… 此处写了10行
//获取最近的一次订单信息
… 此处写了30行
// 获取钱包余额、可用优惠券张数等
… 此处写了30行
return userDTO;
}
看看正例:
// 获取个人信息
private UserDTO getUserDTO(Integer userId)
{
//获取基本信息
UserDTO userDTO= getUserBasicInfo(userId);
//获取最近的一次订单信息
userDTO.setUserLastOrder(getUserLastOrder(userId));
// 获取钱包、可用优惠券张数等
userDTO.setUserAccount(getUserAccount(userId));
return userDTO;
}
private UserDTO getUserBasicInfo(Integer userId);
private UserLastOrder getUserLastOrder(Integer userId);
private UserAccount getUserAccount(Integer userId);
减少if/else嵌套
为什么要减少嵌套,嵌套看起来不时髦吗?我曾经看到一个同事的代码嵌套了多达9层,当他自己维护代码时,他感到头晕目眩。代码过度嵌套的结果是,只有原始作者才能阅读它。
看看反例:
// 修改用户密码,这个例子只有3层嵌套,很温柔了
public boolean modifyPassword(Integer userId, String oldPassword, String newPassword) {
if (userId != null && StringUtils.isNotBlank(newPassword) && SpringUtils.isNotBlank(oldPassword)) {
User user = getUserById(userId);
if (user != null) {
if (user.getPassword().equals(oldPassword) {
return updatePassword(userId, newPassword)
}
}
}
}
看看正例:
// 修改用户密码
Public Boolean modifyPassword(Integer userId, String oldPassword, String newPassword) {
if (userId == null || StringUtils.isBlank(newPassword) || StringUtils.isBlank(oldPassword)) {
return false;
}
User user = getUserById(userId);
if(user == null) {
return false;
}
if(!user.getPassword().equals(oldPassword) {
return false;
}
return updatePassword(userId, newPassword);
}
正面示例使用了guard语句来减少嵌套,但并不是所有场景都适合这样做。如果没有,可以将高度相关的逻辑提取到单独的方法中,以减少嵌套。
抽离try/catch
你可曾见过一段很长的方法要从开始到结束由try/catch照顾?在小猪站长所经历的项目中,这种不负责任的写作方式随处可见。不是每一行代码都会抛出错误,只要将抛出错误的业务放在单独的方法中即可。
看看反例:
// 获取个人信息
private UserDTO getUserDTO(Integer userId)
{
try {
//获取基本信息
... 此处写了10行
//获取最近的一次订单信息.
...此处写了20行
// 获取钱包、可用优惠券张数等
...此处写了20行
}catch (Exception e) {
logger.error(e);
return null;
}
}
return userDTO;
}
看看正例:
// 获取个人信息
private UserDTO getUserDTO(Integer userId)
{
//获取基本信息
UserDTO userDTO= getUserBasicInfo(userId);
//获取最近的一次订单信息
userDTO.setUserLastOrder(getUserLastOrder(userId));
// 获取钱包、可用优惠券张数等
userDTO.setUserAccount(getUserAccount(userId));
return userDTO;
}
private UserDTO getUserBasicInfo(Integer userId);
private UserLastOrder getUserLastOrder(Integer userId);
private UserAccount getUserAccount(Integer userId){
try{
// TODO
} catch ( Exception e)
{ //TODO }
}
封装多个参数
如果方法参数超过3,建议将它们封装在类中。否则,当添加参数时,调用者的语法错误将由于语义的强耦合而导致。在后台管理的分页管理接口中,查询参数往往较多,并且有可能增加,且包是最好的。
看看反例:
// 分页查询订单 6个参数
public Page<Order> queryOrderByPage(Integer current,Integer size,String productName,Integer userId,Date startTime,Date endTime,Bigdecimal minAmount ,Bigdecimal maxAmount) {
}
看看正例:
@Getter
@Setter
public class OrderQueryDTO extends PageDTO {
private String productName;
private Integer userId;
private Date startTime;
private Date endTime;
private Bigdecimal minAmount ;
private Bigdecimal maxAmount;
}
// 分页查询订单 6个参数
Public Page<Order> queryOrderByPage(OrderQueryDTO orderQueryDTO) {
}
第三方库
Lombok
Lombok组件通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法
举例如下:
@Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
@Getter 使用方法同上,区别在于生成的是getter方法。
@ToString 注解在类,添加toString方法。
@EqualsAndHashCode 注解在类,生成hashCode和equals方法。
@NoArgsConstructor 注解在类,生成无参的构造方法。
@RequiredArgsConstructor 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
@AllArgsConstructor 注解在类,生成包含类中所有字段的构造方法。
@Data 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
常规写法:
Public class Order {
private Integer userId;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
return this.userId = userId;
}
}
采用Lombok:
@Getter
@Setter
Public class Order {
private Integer userId;
}
Apache Commons系列
Apache Commons组件家族为我们提供了用于字符串、集合和IO操作的工具和方法。这些部件是一个巨大的财富,提供了许多车轮。
该组件引入了beanUtils JavaBean来执行各种操作、克隆对象、属性等。用于处理常用编码方法的编解码工具包,如DES、SHA1、MD5、Base64等集合java集合框架操作配置java应用程序配置管理类库io io工具封装了工具类包的lang java基本对象方法,如StringUtils、ArrayUtils等。日志提供的日志接口提供了一个客户端和服务器端数据验证框架来查看示例:
例1: 判断集合是否为空:
CollectionUtils.isEmpty(null): true
CollectionUtils.isEmpty(new ArrayList()): true
CollectionUtils.isEmpty({a,b}): false
例2: 判断集合是否不为空:
CollectionUtils.isNotEmpty(null): false
CollectionUtils.isNotEmpty(new ArrayList()): false
CollectionUtils.isNotEmpty({a,b}): true
例3:2个集合间的操作:
集合a: {1,2,3,3,4,5}
集合b: {3,4,4,5,6,7}
CollectionUtils.union(a, b)(并集): {1,2,3,3,4,4,5,6,7}
CollectionUtils.intersection(a, b)(交集): {3,4,5}
CollectionUtils.disjunction(a, b)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.disjunction(b, a)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.subtract(a, b)(A与B的差): {1,2,3}
CollectionUtils.subtract(b, a)(B与A的差): {4,6,7}
评论 (0人参与)
最新评论