`
chhj_292
  • 浏览: 37555 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

分享一些编码技巧

阅读更多

 

分享一些编码技巧

 

 

Author : chenghaojun

 

 

 

 

 

说明

 

下面我分享一些我知道的,能很明显的使代码结构更加清晰的编码技巧。其中的很多方式都是我阅读其他开源项目的源码或者书籍所获得的经验。

 

 

我的示例代码全部来自于淘江湖现有的主干代码。当然,不会涉及到任何关于代码作者的信息。而且我这里展示这些代码只是出于现实的示例考虑,不存在任何其他想法。

 

 

我阐述的以下技巧不局限于在java的编码中,我的本意是提供一些解决方案尽量适用于所有的编码生活。

 

 

编码技巧

 

使用卫述句

 

卫述句是最容易掌握也是能获取最大效果的编码技巧之一。所以我这里把它列在第一位。

 

 

卫述句最大的作用就是能避免代码的深层嵌套(深层嵌套的代码带来的后果是不是本文的重点)。容易造成代码不必要的深层嵌套的场景包括:if-else嵌套,for,while迭代等。所以其使用场景大部分存在于判断逻辑(if语句)和迭代逻辑(for,while,etc)中。

 

 

注意:如果你使用了卫述句代表你结束了一个异常流程。而终止流程的情况总是returnthrow语句,所以使用卫述句必须多花点精力在这两个语句上。


范例

 

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);
 

 

 

 

 

迭代中灵活使用continue,return,break

 

记得我上学初学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());
	}});
}
 

 

一些感想

 

写完这个文档的时候,我突然想到有一个笑话:经验丰富的老医生要研究,实战丰富的医生要演讲、出书……,剩下实习医生要拿病人练手。

 

 

好吧,我修改了一下,用来形容我们现在的开发情况很合适:经验丰富的老程序员要做架构,编码颇为优秀的程序员要做PMTL,剩下的只有实习程序员编码练手。

 

 

3
0
分享到:
评论
2 楼 叫我Fox 2011-08-10  
过多使用嵌套的判断逻辑 看代码却是也累
1 楼 coolcat9527 2010-07-14  
LZ很强大

相关推荐

    基于Go语言的编码技巧与实战设计源码

    该项目是专注于Go语言编码技巧与实战设计的源码集,包含194个文件,涵盖166个Go源文件、7个Protocol Buffers定义文件、3个XML配置文件、2个Git忽略规则文件、2个PNG图片文件、2个PDF文档、2个HTML页面、2个Markdown...

    OrBit-MES信息编码指导书

    指导书深入探讨了物料概念,将其分为原材料、半成品、成品等多种类型,并分享了编码过程中的经验和技巧,帮助读者更好地理解并应用编码原则。 ### 编码方法参考 指导书提供了多种编码方法的介绍,包括自然编码法和...

    STM32电机控制例程分享 第十期 (步进电机编码器读取)

    通过这个例程,学习者不仅可以掌握STM32的基本编程技巧,还能深入理解电机控制中的关键概念,如编码器的工作原理和应用,这对于提升嵌入式系统设计能力非常有帮助。同时,实例化的代码有助于快速应用于实际项目,...

    C#中几个未知的Visual Studio编码技巧分享

    用了多年的Visual Studio,今天才发现这个编码技巧,真是惭愧,分享出来,算是抛砖引玉吧! 开发环境: vs2010+C#1、代码重构新建类 如果你还像我以前一样使用右键快捷菜单新建类,那就太Out了。VS的那个“新建项...

    C、C++和Java安全编码实践提示与技巧

    ### C、C++和Java安全编码实践提示与技巧 #### 注入攻击防范 在软件开发过程中,特别是使用C、C++或Java等语言时,安全编码至关重要。本篇重点介绍几种常见的安全问题及其防范措施,尤其关注的是如何避免注入攻击...

    赫夫曼编码译码器的全部源代码(基于C#编码

    通过阅读和理解这些源代码,可以加深对数据压缩算法的理解,提升C#编程技巧,并有可能启发新的项目构思和解决方案。 总的来说,这份基于C#的赫夫曼编码译码器源代码集合不仅提供了实际的数据压缩工具,还是一本生动...

    《编码的奥秘》PDF书

    PDF文档形式使得《编码的奥秘》易于分享和传播,同时也便于读者做笔记和搜索。读者可以利用PDF阅读器的高亮、批注功能,记录下自己的理解和感悟。 总之,《编码的奥秘》是一本深度和实用性兼备的编程书籍,它涵盖了...

    数据结构课程设计实验报告(赫夫曼编码).doc

    数据结构课程设计实验报告主要关注的是赫夫曼编码的实现,这是一种用于数据...通过这个课程设计,学生不仅能够加深对数据结构的理解,尤其是二叉树和哈夫曼编码的概念,还能提升编程实践能力,掌握算法设计与实现技巧。

    报关讲座 2.15编码

    6. 案例分析:通过具体的报关案例,解析编码选择的复杂性和技巧,帮助听众理解和掌握实际操作中的问题解决方法。 7. 法规和合规性:讲解各国海关对编码使用的法规要求,以及违反规定可能导致的法律后果。 8. 常见...

    语音编码语音编码--4

    总之,语音编码是一个复杂且关键的技术领域,涵盖了多种理论和实践技巧。深入理解CELP等编码方法,结合相关的学习资源,如“www.pudn.com.txt”提供的信息,能够帮助我们更好地掌握这一领域的知识,并在实际应用中...

    git-lesson:直接从您的提交中分享您的编码技巧和课程

    混帐课 WIP在部署之前还有一些调整要做! 直接从您的提交中分享编码技巧和课程! 去做 Github 登录 挂钩设置 钩子有效载荷接收和处理 供稿视图 基于标签的视图 实时查看

    安全编码实战经验.pdf

    在“安全编码实战经验.pdf”中,我们可以看到作者分享了一些关于如何在实际开发中应用安全编码原则和技术的知识点,尤其是针对PHP编程语言和Linux环境。 首先,文档提到了C语言的栈溢出攻击。栈溢出是由于函数调用...

    utf-8编码的中文字符在source in sight下的解决方法

    在IT行业中,编码问题是一个常见的挑战,尤其是在跨平台的工作环境中。这里我们关注的是如何在Source Insight这款强大的源代码查看和...对于经常需要在跨平台间切换的开发者来说,熟悉各种编码转换技巧是非常有益的。

    零缺陷”开发技巧

    下面,我们将深入探讨一些实现零缺陷开发的关键技巧。 1. **需求清晰**:确保对项目需求有深入的理解,这是避免错误的第一步。需求分析阶段应详细、准确,避免模糊不清或者遗漏。 2. **设计先行**:在编码之前进行...

    分享那些有趣的 Bug。推荐一些有意思的前端技巧。聊聊前端面试等。.zip

    这里我们将探讨一些前端开发中的常见问题、技巧和面试策略,希望能为你提供宝贵的参考。 首先,前端开发中的Bug往往源于各种原因,如浏览器兼容性问题、JavaScript语法理解错误、DOM操作不当等。例如,CSS的盒模型...

Global site tag (gtag.js) - Google Analytics