Author : chenghaojun
说明
下面我分享一些我知道的,能很明显的使代码结构更加清晰的编码技巧。其中的很多方式都是我阅读其他开源项目的源码或者书籍所获得的经验。
我的示例代码全部来自于淘江湖现有的主干代码。当然,不会涉及到任何关于代码作者的信息。而且我这里展示这些代码只是出于现实的示例考虑,不存在任何其他想法。
我阐述的以下技巧不局限于在java的编码中,我的本意是提供一些解决方案尽量适用于所有的编码生活。
编码技巧
使用卫述句
卫述句是最容易掌握也是能获取最大效果的编码技巧之一。所以我这里把它列在第一位。
卫述句最大的作用就是能避免代码的深层嵌套(深层嵌套的代码带来的后果是不是本文的重点)。容易造成代码不必要的深层嵌套的场景包括:if-else嵌套,for,while迭代等。所以其使用场景大部分存在于判断逻辑(if语句)和迭代逻辑(for,while,etc)中。
注意:如果你使用了卫述句代表你结束了一个异常流程。而终止流程的情况总是return和throw语句,所以使用卫述句必须多花点精力在这两个语句上。
com.taobao.matrix.friend.app.web.home.module.action.friend.FriendRelationAction.doUpdateFriendDynamicSet
原始代码:
try {
int flag = rundata.getParameters().getInt("flag", -1);
String friendIds = null;
List<Long> friendsList = new ArrayList<Long>();
if (!friendsList.isEmpty()) {
Result result;
if (flag != -1) {
if (flag == 0) {//屏蔽好友动态
// ...
} else if (flag == 1) {//解除屏蔽好友动态
// ...
} else if (flag == 2) {//修改部分动态屏蔽列表
// ...
}
}
}
} catch (Exception e) {
logger.error(e);
}
以上是FriendRelationAction.java中的一段代码(有删节),这段代码使用了3个嵌套语句。仔细看一下,其中有两个if语句(用下划线标注)是不友好的,两个判断逻辑都是为了确定一个正常逻辑,从而带来了代码毫无必要的两层次嵌套。
改进的措施是,确定异常逻辑,处理完异常逻辑之后,使用return或者throw直接终止异常流。解决代码的嵌套问题。在这个场景中,优化完的代码没有任何嵌套了。
优化代码:
try {
int flag = rundata.getParameters().getInt("flag", -1);
String friendIds = null;
List<Long> friendsList = new ArrayList<Long>();
if (friendsList.isEmpty()) {
throw new IllegalStateException("friends is empty!");
}
if (flag == -1) {
return;
}
if (flag == 0) {//屏蔽好友动态
// ...
} else if (flag == 1) {//解除屏蔽好友动态
// ...
} else if (flag == 2) {//修改部分动态屏蔽列表
// ...
}
} catch (Exception e) {
logger.error(e);
}
看了上面的这些说明和示例,知道如何优化下面这段代码了吗?
com.taobao.matrix.exchange.biz.service.impl.AwardHomeServiceImpl.queryMyAwardHome
// 3. 判断查询出来的过期数据是否为空,
// 如果不为空,把过期的数据放到总的id列表中
// 如果为空 ,直接把没有过期的数据放到数据目标list里
if (totalExpiredIdList != null) {
totalList.addAll(totalExpiredIdList);
if (totalList.size() > awardQuery.getPageSize()) {
// 1)对数据进行截取,去除多余的过期数据
rntList.addAll(totalList.subList(0, awardQuery
.getPageSize()));
} else {
// 2)如果把剩余数据放到目标列表中
rntList.addAll(totalList);
}
} else {
// 如果把剩余数据放到目标列表中
rntList.addAll(totalList);
|
记得我上学初学c语言的时候,其中的循环是让我最纠结的部分之一。特别是对于迭代中的++,--操作符,简直是一团糟。尽管现在已经不存在上学时的问题,但是碰到循环我还是条件反射一直想办法让循环中的代码变得简洁一点,这样有助于更好的理解代码。
其实continue,return,break这三个语句基本上是任何编程语言都具有的逻辑控制语句,迭代中如果能灵活的运用这三个语句,很良好的控制循环中嵌套以及逻辑复杂程度。
原始代码:
com.taobao.matrix.ac.service.SnsUserQueryServiceImpl.parseTerminatorResponse
for (DocumentDO doc : docs) {
List<FieldDO> fields = doc.getFields();
if (fields != null) {
QueryUserInfo user = new QueryUserInfo();
// other operates
userList.add(user);
}
}
other operates注释这里我省去了若干代码,现实中的代码这个方法中有5层嵌套!看这个丑陋的IF语句做了什么?把本来没有必要进行嵌套的代码块加入了他所管辖的范围。
下面我用一个continue语句来优化他。把嵌套了的代码释放出来。
优化代码:
for (DocumentDO doc : docs) {
List<FieldDO> fields = doc.getFields();
if (fields == null || fields.size() == 0) {
continue;
}
QueryUserInfo user = new QueryUserInfo();
// other operates
userList.add(user);
}
这个编码技巧是使用了“散发着魔力的大括号”,我第一次是看见(Bob Lee)在使用这种方式。
我们假设的场景只是纯粹的临时性需要。临时性我理解也就是一次操作之后对象绝大部分情况是销毁掉了的场景。例如:对象作为参数、对象装入集合中的情况等等。
说明这个实例之前,我们先设想一下如下场景,我们需要一个SnsUser对象,可能是某个接口需要这个对象并且要使用这个对象中的5个属性,例如userId,realName,nick等。
如果你读懂了题意,你创建这个对象的方式是什么,是不是如下所示?
com.taobao.matrix.uc.SnsUser
SnsUser u = new SnsUser();
u.setUserId(1l);
u.setNick("nick");
u.setRealName("realName");
method.invoke(u);
|
相信这也是绝大部分人的处理方式。但是我们何必冒着被滥用的危险去维护一个临时性的、甚至是有些多余的SnsUser实例呢。如果这个对象是临时性的,有一种更加友好的方式来创建这个对象。
请看下表:
method.invoke(new SnsUser() {{//散发着魔力的大括号
setUserId(1l);
setNick("nick");
setRealName("realName");
}});
这样的处理方式,在实现一个匿名类的时候是一个常用手段,我也是在阅读xwork2 injection 代码的时候,偶然发现了天才的google程序员竟然用这种新的方式创建对象。我第一次看见(Bob Lee)使用这种方式(不要尝试联系他)。
这种方式尤其适合使用Ibatis时,传入一个Map作为参数的情况,如:
getDaoSupport().execute(new HashMap<String, String>() {
{
put("params1", "...");
put("params2", "...");
put("params3", "...");
put("params4", "...");
}
});
com.taobao.matrix.ac.service.SnsPluginCenterServiceImpl.findPluginDataByUser
原始代码:
for (final PluginDataDO pd : list) {
res = new PluginData();
res.setId(pd.getId());
res.setUserId(pd.getUserId());
res.setPluginId(pd.getPluginId());
res.setTitle(pd.getTitle());
res.setIcon(pd.getIcon());
res.setGmtCreate(pd.getGmtCreate());
res.setGmtModified(pd.getGmtModified());
res.setIsDeleted(pd.getIsDeleted());
result.add(res);
}
PluginData的实例res只是迭代中的一个临时对象,res这个实例在这个迭代的逻辑里是完全多余的。在迭代代码块外申明变量本身也是危险的,这个本来只在迭代内部使用的变量给其他的滥用提供了便利。
我们去掉这个临时的实例声明吧,避免重复和滥用。
优化代码:
for (final PluginDataDO pd : list) {
result.add(new PluginData() {{
setId(pd.getId());
setUserId(pd.getUserId());
setPluginId(pd.getPluginId());
setTitle(pd.getTitle());
setIcon(pd.getIcon());
setIcon(pd.getIcon());
setGmtCreate(pd.getGmtCreate());
setGmtModified(pd.getGmtModified());
setIsDeleted(pd.getIsDeleted());
}});
}
一些感想
写完这个文档的时候,我突然想到有一个笑话:经验丰富的老医生要研究,实战丰富的医生要演讲、出书……,剩下实习医生要拿病人练手。
好吧,我修改了一下,用来形容我们现在的开发情况很合适:经验丰富的老程序员要做架构,编码颇为优秀的程序员要做PM,TL,剩下的只有实习程序员编码练手。
相关推荐
用了多年的Visual Studio,今天才发现这个编码技巧,真是惭愧,分享出来,算是抛砖引玉吧! 开发环境: vs2010+C#1、代码重构新建类 如果你还像我以前一样使用右键快捷菜单新建类,那就太Out了。VS的那个“新建项...
混帐课 WIP在部署之前还有一些调整要做! 直接从您的提交中分享编码技巧和课程! 去做 Github 登录 挂钩设置 钩子有效载荷接收和处理 供稿视图 基于标签的视图 实时查看
适合任何一位基于JavaScript开发的开发者。我写这篇文章主要涉及JavaScript中一些简写的代码,帮助大家更好理解一些JavaScript的基础。希望这些代码能从不同的角度帮助你更好的理解JavaScript。
一个Python工匠:来自一位Pythonista的编程经验分享,内容涵盖编码技巧,最佳实践与思维模式等方面
Python的可读性和设计简单性是其广受欢迎的两个主要原因。文中通过实例给大家介绍的很清楚,本文重点给大家分享18个Python脚本可加速你的编码速度,感兴趣的朋友一起看看吧
主要介绍了20个实用的JavaScript技巧分享,本文讲解的都是开发中总结出的编码技巧、和最佳实践,需要的朋友可以参考下
主要介绍了14个有用的Jquery技巧分享,本文有一些性能优化技巧、编码优化技巧、简洁方法等,需要的朋友可以参考下
编码采访101 作者:@xianzhez 上次更新时间:2020.11.26 评论: ... 如果您觉得有用,欢迎加注星标,分享和共享此仓库。 如果您要参考此仓库,也请提供参考。 编码经典算法可能无法代表您的实际
一阵事情有点多,没来的及更新神马东西。今天分享两个ppt吧。主要内容是关于一些编码的小技巧,另一个关于前端架构的一些些东西。
笔者深感自己的开发能力不足,因此希望能够将这些使用技巧记录下来,同时也把这些使用技巧分享给读者,希望能对读者有所帮助。 以下将介绍几个简单的PyCharm使用技巧。以Mac系统中的PyCharm使用为例。 自定义文件头 ...
学好css必须掌握的8个布局技巧,使你形成良好的编码规范。。网络收集来的。。 分享
编码规范 代码自动检测插件 系统优化知乎专栏 常用集合 Java 多线程 JVM 分布式相关 常用框架\第三方组件 架构设计 DB 相关 Netty 相关 附加技能 联系作者 qianjinfu#gmail.com 欢迎加入 铁杆粉丝 的知识星球 分享...
15.1 鼠标操作技巧 390 0626 鼠标双击窗体时模拟键盘Tab键操作 390 0627 定义鼠标指针形状 390 0628 自定义鼠标的图片 391 0629 鼠标拖放复制文本 391 0630 如何使用鼠标单击添加控件 392 0631 如何...
最近在读阿当的《Web前端开发修炼之道》 , 其中有不少东西值得前端路上的朋友学习. 结合自己日常编码的一些经验, 我将陆陆续续将一些从书中学到的以及自己总结的一些前端方面的技巧分享给大家.
我们涵盖了广泛的主题,并分享了交易的工具和技巧! 阅读文章,按标签浏览,评论您的想法,并与您的朋友分享! 欢迎来到黑暗艺术!使用的技术前端: JavaScript Next.js React.js Redux.js Redux传奇Firebase身份...
python 在linux中需要添加编码方式:以免出现中文乱码 # -*- coding: UTF-8 –*- 二 在各类语言中,python应该是最会利用识缩进的语言 ,他的for语句即使有多行也不需要想java,C++、c一样使用{} ,可以像js、...
本文总结分析了MySQL查询优化的技巧。分享给大家供大家参考,具体如下: 熟悉SQL语句的人都清楚,如果要对一个任务进行操作的话,SQL语句可以有很多种相关写法,但是不同的写法查询的性能可能会有天壤之别。 本文...