- 浏览: 538548 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
飞天奔月:
public List<String> gener ...
实践中的重构30_不做油漆匠 -
在世界的中心呼喚愛:
在世界的中心呼喚愛 写道public class A {
...
深入理解ReferenceQueue GC finalize Reference -
在世界的中心呼喚愛:
在世界的中心呼喚愛 写道在世界的中心呼喚愛 写道在classB ...
深入理解ReferenceQueue GC finalize Reference -
在世界的中心呼喚愛:
在世界的中心呼喚愛 写道在classB的finalize上打断 ...
深入理解ReferenceQueue GC finalize Reference -
在世界的中心呼喚愛:
iteye比较少上,如果可以的话,可以发e-mail交流:ch ...
深入理解ReferenceQueue GC finalize Reference
很多应用程序的主要目的就是用计算机来代替人处理真实世界中的问题。真实世界和计算机世界之间有着巨大的差异。编程语言,编程方法一直以来都有一个重要的发展方向,提供一个平台,尽量减少这个差异。面向对象语言,面向领域编程,为解决该问题在不同层次提供了解决方案。
在代码级别,也可以通过种种方式减少这个差异。
先看下面一段代码,需求和场景是这样的。
查询用户列表,传入一个整数,指定一次查询返回用户数的上限。
有3个不同的数据源可以用来查询用户信息,这3个数据源在查询的时候是有优先级的。即先查询第1个数据源,如果得到的用户数少于上限,则到第2个数据源查询,如果还是少于上限,则到第3个数据源查询。
如果你耐心的看完了这段代码,我只能说佩服了。一眼看到5层的if嵌套,我已经是半晕状态了。我承认,我没有耐心看完这段代码。
该方法的需求和场景都是比较简单的,因此应该存在简单的写法。
重写的代码如下:
这里首先对参数加上final修饰符,明确表明方法中不会修改该参数。其次,去除掉了if嵌套,用相似的代码结构试图更清晰更直白的表明这段代码的意图。
这段代码有这样一些小的问题,可以持续改进。
1 如果可以预期该方法返回的用户列表大小的话,可以设置初始的列表大小。
2 如果遵循查询结果为空,返回空列表而不是null的话,可以是代码更紧凑。
这样看上去好多了。这里遵循的原则就是平铺直叙的用编程语言翻译需求,试图用接近自然语言的表达方式实现需求,从而减少两个世界的差异。
更好的一种方式是先用自然语言(或者伪代码)描述需求的实现,然后翻译成真正的代码。
用自然语言描述这个需求的实现如下:
1 新建一个空的用户列表。
2 计算下一次查询的上限。
3 到数据源1去查询。
4 添加结果到用户列表。
5 如果用户列表达到上限则返回。
6 计算下一次查询的上限。
7 到数据源2去查询。
8 添加结果到用户列表。
9 如果用户列表达到上限则返回。
10 计算下一次查询的上限。
11 到数据源3去查询。
12 添加结果到用户列表。
13 返回。
这个实现的描述和真实的实现很像,即使没有实现,看着这个自然语言的实现翻译成对应的代码实现也很容易。
当然,计算机编程是复杂的,这里的实现还是有一个潜在的问题。代码中有重复的结构。鉴于这里的实现已经比较简单了,所以没有进一步去掉这个重复的结构。但是,如果需求改变,需要查询更多的数据源,或者可配置的数据源,代码可以做以下演进。
在方法体中,方式1和方式2都是可行的。对于简单的循环,尾部的一个判断跳出看上去比一个多行的条件判断更清晰一点。
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
既然是讲“重构”,多余的代码就需要清理掉,否则就不是“平铺直叙”。
这个有一定的道理,但是我觉得还是有商量的余地。
这个和下面的代码块的结构保持一致。从语义上保持一致。
翻译一下三个代码块对当前要查询的用户数的处理
1 为用户数上限数减去已经查找到的用户数。
2 为用户数上限数减去已经查找到的用户数。
3 为用户数上限数减去已经查找到的用户数。
而省掉这个,是建立在这个是第一次查询,已经知道已经查找到的用户数为0。
等于说,
1 为用户数上限数(第一次默认知道已经查找到的用户数为0)。
2 为用户数上限数减去已经查找到的用户数。
3 为用户数上限数减去已经查找到的用户数。
我选择第一种,是因为看上去虽然有一点冗余,但是这个冗余增强了代码含义的一致性。
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
既然是讲“重构”,多余的代码就需要清理掉,否则就不是“平铺直叙”。
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
get底层的服务,别人未必想的那么周全,如果它的注释中,没有声明一定不返回null的话,我觉得判一下null是比较安全的做法。
在代码级别,也可以通过种种方式减少这个差异。
先看下面一段代码,需求和场景是这样的。
查询用户列表,传入一个整数,指定一次查询返回用户数的上限。
有3个不同的数据源可以用来查询用户信息,这3个数据源在查询的时候是有优先级的。即先查询第1个数据源,如果得到的用户数少于上限,则到第2个数据源查询,如果还是少于上限,则到第3个数据源查询。
public List<User> getUserList_0(int maxSize) { List<User> userList; int selectSize = 0; userList = getUserListFromSource1(maxSize); if (null != userList) { if (userList.size() < maxSize) { selectSize = maxSize - userList.size(); List<User> userList2 = getUserListFromSource2(selectSize); if (null != userList2 && !userList2.isEmpty()) { userList.addAll(userList2); if (userList.size() < maxSize) { selectSize = maxSize - userList.size(); List<User> userList3 = getUserListFromSource3(selectSize); if (null != userList3 && !userList3.isEmpty()) { userList.addAll(userList3); } } } } } else { userList = getUserListFromSource2(maxSize); if (userList.size() < maxSize) { selectSize = maxSize - userList.size(); List<User> userList4 = getUserListFromSource3(selectSize); if (null != userList4 && !userList4.isEmpty()) { userList.addAll(userList4); } } } return userList; }
如果你耐心的看完了这段代码,我只能说佩服了。一眼看到5层的if嵌套,我已经是半晕状态了。我承认,我没有耐心看完这段代码。
该方法的需求和场景都是比较简单的,因此应该存在简单的写法。
重写的代码如下:
public List<User> getUserList_1(final int maxSize) { List<User> userList = new ArrayList<User>(); List<User> temList; int selectSize = maxSize - userList.size(); temList = getUserListFromSource1(selectSize); if (temList != null) { userList.addAll(temList); } if (userList.size() >= maxSize) { return userList; } selectSize = maxSize - userList.size(); temList = getUserListFromSource2(selectSize); if (temList != null) { userList.addAll(temList); } if (userList.size() >= maxSize) { return userList; } selectSize = maxSize - userList.size(); temList = getUserListFromSource3(selectSize); if (temList != null) { userList.addAll(temList); } return userList; }
这里首先对参数加上final修饰符,明确表明方法中不会修改该参数。其次,去除掉了if嵌套,用相似的代码结构试图更清晰更直白的表明这段代码的意图。
这段代码有这样一些小的问题,可以持续改进。
1 如果可以预期该方法返回的用户列表大小的话,可以设置初始的列表大小。
2 如果遵循查询结果为空,返回空列表而不是null的话,可以是代码更紧凑。
public List<User> getUserList_2(final int maxSize) { List<User> userList = new ArrayList<User>(maxSize); int selectSize = maxSize - userList.size(); userList.addAll(getUserListFromSource1(selectSize)); if (userList.size() >= maxSize) { return userList; } selectSize = maxSize - userList.size(); userList.addAll(getUserListFromSource2(selectSize)); if (userList.size() >= maxSize) { return userList; } selectSize = maxSize - userList.size(); userList.addAll(getUserListFromSource3(selectSize)); return userList; }
这样看上去好多了。这里遵循的原则就是平铺直叙的用编程语言翻译需求,试图用接近自然语言的表达方式实现需求,从而减少两个世界的差异。
更好的一种方式是先用自然语言(或者伪代码)描述需求的实现,然后翻译成真正的代码。
用自然语言描述这个需求的实现如下:
1 新建一个空的用户列表。
2 计算下一次查询的上限。
3 到数据源1去查询。
4 添加结果到用户列表。
5 如果用户列表达到上限则返回。
6 计算下一次查询的上限。
7 到数据源2去查询。
8 添加结果到用户列表。
9 如果用户列表达到上限则返回。
10 计算下一次查询的上限。
11 到数据源3去查询。
12 添加结果到用户列表。
13 返回。
这个实现的描述和真实的实现很像,即使没有实现,看着这个自然语言的实现翻译成对应的代码实现也很容易。
当然,计算机编程是复杂的,这里的实现还是有一个潜在的问题。代码中有重复的结构。鉴于这里的实现已经比较简单了,所以没有进一步去掉这个重复的结构。但是,如果需求改变,需要查询更多的数据源,或者可配置的数据源,代码可以做以下演进。
interface UserQueryService { List<User> queryUserList(int maxSize); } class UserQueryServiceImpl_1 implements UserQueryService { @Override public List<User> queryUserList(int maxSize) { return getUserListFromSource1(maxSize); } } class UserQueryServiceImpl_2 implements UserQueryService { @Override public List<User> queryUserList(int maxSize) { return getUserListFromSource2(maxSize); } } class UserQueryServiceImpl_3 implements UserQueryService { @Override public List<User> queryUserList(int maxSize) { return getUserListFromSource3(maxSize); } } public List<User> getUserList_3(final int maxSize) { // 这里的userQueryServices可以从配置文件中生成,也可以用户传入。 // 这里为了演示,硬编码一下。 List<UserQueryService> userQueryServices = new ArrayList<UserQueryService>(); userQueryServices.add(new UserQueryServiceImpl_1()); userQueryServices.add(new UserQueryServiceImpl_2()); userQueryServices.add(new UserQueryServiceImpl_3()); List<User> userList = new ArrayList<User>(maxSize); // 方式1 for (UserQueryService service : userQueryServices) { int selectMaxSize = maxSize - userList.size(); userList.addAll(service.queryUserList(selectMaxSize)); if (userList.size() >= maxSize) { break; } } // 方式2 for (int i = 0; i < userQueryServices.size() && userList.size() < maxSize; i++) { UserQueryService service = userQueryServices.get(i); int selectMaxSize = maxSize - userList.size(); userList.addAll(service.queryUserList(selectMaxSize)); } return userList; }
在方法体中,方式1和方式2都是可行的。对于简单的循环,尾部的一个判断跳出看上去比一个多行的条件判断更清晰一点。
评论
5 楼
tuti
2010-12-19
建议楼上,找个同事来看这2种代码。
听听对方的反馈,哪一种更容易理解。
听听对方的反馈,哪一种更容易理解。
4 楼
zhang_xzhi_xjtu
2010-12-19
tuti 写道
zhang_xzhi_xjtu 写道
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
既然是讲“重构”,多余的代码就需要清理掉,否则就不是“平铺直叙”。
这个有一定的道理,但是我觉得还是有商量的余地。
int selectSize = size - userList.size();
这个和下面的代码块的结构保持一致。从语义上保持一致。
翻译一下三个代码块对当前要查询的用户数的处理
1 为用户数上限数减去已经查找到的用户数。
2 为用户数上限数减去已经查找到的用户数。
3 为用户数上限数减去已经查找到的用户数。
而省掉这个,是建立在这个是第一次查询,已经知道已经查找到的用户数为0。
等于说,
1 为用户数上限数(第一次默认知道已经查找到的用户数为0)。
2 为用户数上限数减去已经查找到的用户数。
3 为用户数上限数减去已经查找到的用户数。
我选择第一种,是因为看上去虽然有一点冗余,但是这个冗余增强了代码含义的一致性。
3 楼
tuti
2010-12-18
zhang_xzhi_xjtu 写道
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
既然是讲“重构”,多余的代码就需要清理掉,否则就不是“平铺直叙”。
2 楼
zhang_xzhi_xjtu
2010-12-18
tuti 写道
Line 6, 多余。
返回UserList的方法都设计的不好, 应该不需要判null。
返回UserList的方法都设计的不好, 应该不需要判null。
line6是多余的,只是为了保持结构一致才这么写的,个人行为。
get底层的服务,别人未必想的那么周全,如果它的注释中,没有声明一定不返回null的话,我觉得判一下null是比较安全的做法。
1 楼
tuti
2010-12-18
Line 6, 多余。
返回UserList的方法都设计的不好, 应该不需要判null。
返回UserList的方法都设计的不好, 应该不需要判null。
发表评论
-
实践中的重构32_使用标准的IO操作写法。
2012-07-14 18:42 1447看到这样一段代码,功能为读取一个指定文件的内容然后返回。 ... -
实践中的重构31_结果类两种实现的比较
2011-09-13 19:58 1099在查询接口结果类设计 ... -
实践中的重构30_不做油漆匠
2011-08-15 23:42 1302油漆匠的故事是编程文化中的一个著名故事。本地化如下。 小强毕业 ... -
实践中的重构29_不自动的自动化测试
2011-07-31 18:00 1071测试的精髓之一就是自 ... -
实践中的重构28_小心怀疑类库
2011-07-24 10:25 1073一般而言,类库的使用频率较高,场景较多,隐藏的bug就较少。 ... -
实践中的重构27_不要忘了内存空间
2011-06-06 18:31 1198方法在设计中,一般关注的是方法的功能契约,即方法需要什么样的参 ... -
实践中的重构26_奇怪的接口注释
2011-06-06 16:10 1366最近又看到奇怪的注释。 /** * 用户查询服务。 ... -
实践中的重构25_UT也需要持续重构
2011-05-01 11:20 1076UT是个好东西,在对代 ... -
实践中的重构24_持续的方法重构
2011-05-01 02:20 1100很少有人可以一遍就写出好的代码。写代码和写文章差不多,大部分人 ... -
实践中的重构23_详尽的注释未必是好注释
2011-03-20 17:37 1565注释一直是软件开发中的一个老大难问题。 代码中一个注释都没有是 ... -
实践中的重构22_不要垃圾
2011-03-20 13:31 1074Java引入了GC当然很好,减轻了程序员手工管理内存的负担,但 ... -
实践中的重构21_给她一个好名字
2011-03-20 13:03 927名字的重要性实在是再怎么强调都不为过的。 为什么名字这么重要呢 ... -
实践中的重构20_一段可笑的异常处理逻辑
2011-03-06 20:32 1723Code review也是一个充满 ... -
实践中的重构19_脱裤子放屁
2011-03-03 23:17 2085每当看到代码中有一个 ... -
实践中的重构18_不对称的美
2011-02-26 22:30 1002一般而言,自然界是以 ... -
实践中的重构17_表驱动法
2011-02-22 00:10 874代码以及初始的单元测试见 http://zhang-xzhi- ... -
实践中的重构16_多即是少
2011-01-16 23:44 1528在编写UT的过程中,随处可见重复,硬编码等等使得代码僵化的代码 ... -
实践中的重构15_null的意义和集合类作为方法结果类型
2011-01-12 22:16 657在编程中,估计null应该是一个很常写的词汇了。 实践中,经常 ... -
实践中的重构14_用方法设计保证正确性
2011-01-04 21:40 1033一般来说,方法的调用方遵循方法的契约调用某方法来完成某功能。每 ... -
实践中的重构13_利用递归提高代码的可维护性
2010-12-30 01:38 748有这么一段代码,是用来解析国内的地址信息的。 AddressI ...
相关推荐
在文字叙述上,本书摒弃了枯燥的平铺直叙,采用案例与问题引导式;同时,本书还增加了“温馨提示”、“例程一点通”、“经验分享”、“一语中的”等版块,彰显了本书以读者为本的人性化的特点。
【思路】:建议大家用2分钟得自我介绍,面试官较喜欢的自我介绍(1)有亮点,每一小段都有一个亮点,而不是平铺直叙(2) 有互动:每一小段都会和面试官互动,而不是自说自话,但是切记,这种互动并不需要面试官配合...
“平铺直叙”则形容一种直接叙述的方式,符合语境;“言不及义”通常指言语空洞、无关紧要,但若用于翻译不准确则有失偏颇;“山高水低”本指意外的不幸,若用于描述领略路途的美景则不太恰当;“目不交睫”形容人因...
C语言作为一门基础且重要的编程语言,其教学方式长期以来多采用传统的平铺直叙方式,导致学生难以产生浓厚的学习热情。针对这一问题,文章提出了以案例为中心的教材改革策略。 C语言自1972年诞生以来,因其功能强大...
由于学生的基础层次不一,教师平铺直叙的讲解方式难以激发学生的兴趣,导致学生对PLC的工作原理和程序设计的理解不深入,难以做到熟练应用和独立思考。这直接影响了学生在实验中的操作能力和创新能力。 2. 缺乏实际...
信息技术学科包含许多专业性的知识和术语,传统的平铺直叙往往难以激发学生的学习兴趣。作者建议在课前设置自主体验任务,例如让学生为Python制作自我介绍,这样既可以使学生带着问题走进课堂,又可以在教师的引导下...
书中内容并非平铺直叙,而是将神经生物学、认知心理学和学习理论等研究领域的最新成果融入教学之中,使读者在探索网页设计的过程中,通过生动有趣的方式,激发学习HTML、CSS和XHTML的兴趣。 HTML是构建网页的骨架,...
5. **概述法**:这是一种按照时间顺序、逻辑层次或因果关系进行讲解的方法,也称作平铺直叙,常用于概括性介绍景点或事件。 6. **触景生情法**:此方法要求导游根据眼前的景色引发情感,借题发挥进行讲解,要求自然...
反问句往往用于强调说话者的强烈情感或明确立场,而陈述句则平铺直叙,更为客观冷静。在实际教学中,教师应注重句式的语境分析,让学生通过实例来理解这些句式的不同用途,而不仅仅是停留在表面形式上的比较。 综上...
初期的习题课和新课教学让实习生积累了宝贵经验,同时也暴露了一些问题,如紧张、讲解过于平铺直叙、课堂控制不够灵活等。通过指导教师的反馈和自我反思,实习生逐渐改进,提高自己的教学水平。 在班主任实习方面,...
教学内容不再仅仅是知识点的平铺直叙,而是围绕核心教学目标和任务进行合理地组织与展开。这种方法有助于学生明确学习目标,减少学习的盲目性,让学生在学习过程中更有方向感和目的性。 对于学生而言,任务驱动教学...
崔老师在未来的教学中可以更加注重语言表达的生动活泼,避免平铺直叙。在讲解作画方法和欣赏作品时,应该注意逻辑顺序,减少语言上的冗余,以提高教学效率。只有这样,才能确保每个学生都能在愉快的氛围中有效学习。...
比如,伯瑙德夫人的智斗故事被详尽描绘,而敌人的简单搜查则一带而过,通过这样的案例分析,学生能够理解如何根据文章主题的需要来确定内容的详略,避免文章内容平铺直叙,缺乏波澜。 同时,教师还提供了多个写作...
传统教学中,教师往往平铺直叙地讲解,而忽视了激发学生兴趣的关键点。例如,李明芳老师在教授《丑小鸭》这一课时,选择了丑小鸭的“不幸”话语作为导入,成功地吸引了学生的注意力,并引导他们深入文本,挖掘背后的...
教师在引导学生进行课堂活动时,有时语言可能过于平铺直叙,缺乏感染力,这可能会让学生们的兴趣有所下降。体态语言作为无声的教育手段,其运用也需要更为精准和多样化,这样才能更有效地抓住学生的眼球,使他们更...
他的小说不满足于平铺直叙地讲述故事,而是将历史的背景和个体的遭遇紧密地结合起来。在这样的叙述中,历史不再是单纯的时间和事件的叠加,而是转化为小说中人物的生活经验和心理状态的投影。双雪涛通过个人命运的...
通过这样的学习,学生可以领悟到在写作中如何运用细节来突出人物性格,而不仅仅是平铺直叙地描述事件。这种写作技巧的学习,是语文教学中不可或缺的部分,它能够有效地提升学生的写作能力和对人物描绘的敏感度。 ...
4. **突出重点和亮点**:在汇报中,强调方案的独特性和价值点,避免平铺直叙。这可以通过使用案例、数据和视觉工具来增强说服力。 5. **共鸣与互动**:尝试与听众建立共鸣,让他们参与到讨论中来,而不是单方面的...
此外,侯方域在描述两位演员较量时,并没有平铺直叙,而是巧妙地通过观众的反应来凸显双方技艺的高下,细节的描写让角色形象更加立体。比如李伶的第一次胜利后,观众的热烈反响以及第二次较量后李伶的谦卑态度,这些...
本书的标题“Head First Python”暗示了其教学方法,即通过直观、互动的方式,以问题和答案的形式引导读者思考和学习,与传统的平铺直叙的教学方法有所不同。这种方法也被其他“Head First”系列书籍所采用,例如在...