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

实践中的重构06_方法调用顺序和性能

阅读更多
大部分的书在讲解涉及到性能优化的时候都告诉我们不要过早优化。简而言之,过早优化是万恶之源。
过早的担心性能问题,不经过性能测试,同时花费大量的时间和精力去在一些主观判断的性能瓶颈点做了一些性能优化的工作,实际上很有可能对性能提高没有什么帮助,而且导致代码可读性直线下降。这样的做法当然是不明智的。
但是,打着过早优化是万恶之源的幌子,从而在写代码的时候压根不考虑性能问题,以为计算机就是万能机器,没有性能问题,一样是不可取的。
在写code的时候,性能还是应该有所考虑的。而且很多时候,对性能问题的考虑并不会花费很多时间,同时也不会把代码搞的一团糟。
	public boolean f_0(String userId) {

		// isLoginAgainInMonth is local call.
		boolean isLoginAgainInMonth = isLoginAgainInMonth(userId);
		// isGardUser is ws call.
		boolean isGardUser = isGardUser(userId);

		return isLoginAgainInMonth || isGardUser;
	}

上面的code判断了一个用户是本月的非第一次登录,或者是一个受保护的用户,是的话返回true,做一些处理。
大眼一看,这段代码当然是没有什么问题的。但是加上性能的考量,问题就出来了。
isLoginAgainInMonth是一个local call,isGardUser是一个ws call,当isLoginAgainInMonth为true的时候是可以直接返回的,多调用一次isGardUser就多一次ws开销,这个开销包括网络开销,内存开销,甚至db开销。如果该代码被大量执行次数放大的话,所消耗的资源还是很可观的。
这里调整代码来强调方法的调用顺序对性能的影响。重构后为:
	/**
	 * Note: 为了性能考量,先调用本地方法,后调用ws服务,当可以判断出结果的时候,尽早返回。
	 * */
	public boolean f_1(String userId) {
		// isLoginAgainInMonth is local call.
		if (isLoginAgainInMonth(userId)) {
			return true;
		}

		// isGardUser is ws call.
		return isGardUser(userId);
	}

还有一种利用短路特性的写法。
	/**
	 * 不推荐的写法。
	 * */
	public boolean f_2(String userId) {
		return isLoginAgainInMonth(userId) || isGardUser(userId);
	}

这种写法利用短路的特性达到了同样的功能,并且看上去代码更简洁。
但是,应该承认的是,并不是所有程序员对短路的特性都掌握了,同时,即使掌握短路的程序员看到这段代码的时候也未必能够意识到这里的短路和性能相关。该写法没有达到强调方法调用顺序和性能的关系的作用,因此不推荐这种写法。
分享到:
评论
33 楼 ldbjakyo 2010-11-18  
tuti 写道
zhang_xzhi_xjtu 写道
tuti 写道
zhang_xzhi_xjtu 写道


		//isLastLoing is local call.
		boolean isLoginAgainInMonth=isLoginAgainInMonth(userId);
		
		if(isLoginAgainInMonth){
			return true;
		}
		
		//isGardUser is ws call.
		boolean isGardUser=isGardUser(userId);
		
		return isGardUser;




这样如何?
	
		return isLoginAgainInMonth(userId)||isGardUser(userId);



这个当然也行,最早我也是这个想法,但是考虑到未必所有的人都知道短路,才用长一点的方式写,这样可以突出早返回这个特性,后来的程序员也就不会忽略这个东西了。



这样如何?
		
		if(is_login_again_in_month(userId)){
			return true;
		}
		return is_gard_user_via_call_ws(userId);





这样很不错的, 我觉得这个有些像 fowler提倡的  replace temp with query
32 楼 抛出异常的爱 2010-11-17  
iamlotus 写道
tuti 写道
月落码农 写道


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。


有个东西叫做自动测试

自动测试又不测性能的
这种地方既然为了优化,还是写个注释吧。如果不写,这段代码我是看不出楼主刻意强调的用意的

性能测试我也写过
@timeout
zhang_xzhi_xjtu 写道
lewisw 写道
首先我想说的是你这个不是重构, 起码不是代码重构, 而是逻辑的重构。


重构的前提是在不改变代码逻辑的前提下,改给代码换一种结构,一增加可读性、可扩展性。。。。。。


你当前的重构明显改变了逻辑,就是第一个方法调用结果如果为true, 就不调用第二个了,或许你后面的isGardUser方法的调用本身也是必须的, 比如会在远程的服务器上生成验证记录什么的。那你这样去掉反而不对。

你现在的重构实际上就是逻辑上重新整理。


不要局限于M的重构定义。

还是要在意的,
不过改变逻辑需要带另一个帽子而已
31 楼 zhang_xzhi_xjtu 2010-11-16  
tuti 写道
iamlotus 写道
tuti 写道
月落码农 写道


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。


有个东西叫做自动测试

自动测试又不测性能的
这种地方既然为了优化,还是写个注释吧。如果不写,这段代码我是看不出楼主刻意强调的用意的



写注释不是问题,
问题是注释要写成什么样。
怎么保证注释和代码能同步更新。


写注释是可以接受的。
但是短路不接受。
30 楼 zhang_xzhi_xjtu 2010-11-16  
lewisw 写道
首先我想说的是你这个不是重构, 起码不是代码重构, 而是逻辑的重构。


重构的前提是在不改变代码逻辑的前提下,改给代码换一种结构,一增加可读性、可扩展性。。。。。。


你当前的重构明显改变了逻辑,就是第一个方法调用结果如果为true, 就不调用第二个了,或许你后面的isGardUser方法的调用本身也是必须的, 比如会在远程的服务器上生成验证记录什么的。那你这样去掉反而不对。

你现在的重构实际上就是逻辑上重新整理。


不要局限于M的重构定义。
29 楼 tuti 2010-11-16  
iamlotus 写道
tuti 写道
月落码农 写道


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。


有个东西叫做自动测试

自动测试又不测性能的
这种地方既然为了优化,还是写个注释吧。如果不写,这段代码我是看不出楼主刻意强调的用意的



写注释不是问题,
问题是注释要写成什么样。
怎么保证注释和代码能同步更新。
28 楼 iamlotus 2010-11-16  
tuti 写道
月落码农 写道


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。


有个东西叫做自动测试

自动测试又不测性能的
这种地方既然为了优化,还是写个注释吧。如果不写,这段代码我是看不出楼主刻意强调的用意的
27 楼 tuti 2010-11-16  
月落码农 写道


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。


有个东西叫做自动测试
26 楼 月落码农 2010-11-16  
gdpglc 写道
chiaun 写道

我认为LZ应该是想让程式码自我注释,如果程式码可以自我注释我认为比明确写上注释要好


不必如果,楼主的代码很明确,你认为达没达到你说的 自我注释的要求?


代码的自我注释的确是很好,这也是大家推荐的做法。
   但是它也有不足,它不可能100%的说明自身,最多60,70%,在很多的模糊场景下,有的隐含条件我觉得必须写明注释,要不一两个月后,也许你自己忘了,更别说别人维护。
25 楼 cleanerje 2010-11-16  
建议弄个简单的权限框架来搞定这个事情,这样维护也很easy。
24 楼 gdpglc 2010-11-16  
trydofor 写道
感觉问题除性能外,还在于:
* WS 或 Heavy方法,在API或项目内没有重视(管理问题)
* 原代码质量不好,接手较痛苦(如,result 魔术) (培训问题)


什么是result魔术?
23 楼 月落码农 2010-11-16  
xuzhfa123 写道
已经想过头了,利用短路方式已经够了。


认可
22 楼 trydofor 2010-11-16  
感觉问题除性能外,还在于:
* WS 或 Heavy方法,在API或项目内没有重视(管理问题)
* 原代码质量不好,接手较痛苦(如,result 魔术) (培训问题)
21 楼 lewisw 2010-11-16  
首先我想说的是你这个不是重构, 起码不是代码重构, 而是逻辑的重构。


重构的前提是在不改变代码逻辑的前提下,改给代码换一种结构,一增加可读性、可扩展性。。。。。。


你当前的重构明显改变了逻辑,就是第一个方法调用结果如果为true, 就不调用第二个了,或许你后面的isGardUser方法的调用本身也是必须的, 比如会在远程的服务器上生成验证记录什么的。那你这样去掉反而不对。

你现在的重构实际上就是逻辑上重新整理。
20 楼 tuti 2010-11-16  
有空都去读一下
《重构》,
第9章 简化条件表达式 (Simplifying Conditional Expressions)
19 楼 dabian_guo 2010-11-16  
首先,所有的书告诉你的应该是:不要过早优化。

其次,你那个只是设计失误而已,算不上性能优化。
18 楼 gdpglc 2010-11-16  
chiaun 写道

我认为LZ应该是想让程式码自我注释,如果程式码可以自我注释我认为比明确写上注释要好


不必如果,楼主的代码很明确,你认为达没达到你说的 自我注释的要求?
17 楼 chiaun 2010-11-16  
gdpglc 写道
zhang_xzhi_xjtu 写道
dwbin 写道
其实觉得代码还是以易读为主,向这样的代码宁肯多写两行,短路的写法是最高效的,但是如果出了问题需要进行单步调试的话还是像楼主那样写起来比较方便。
但是这样写也有个坏处,就是第一个条件被做为了第二个判断的启动条件,不太利于以后的理解,建议还是短路,不过开始的时候增加一个中间变量。
boolean isLoginAgainInMonth=isLoginAgainInMonth(userId);     
return isLoginAgainInMonth||isGardUser(userId);
这样单步调试的时候比较容易控制


这里的纠结在于大家都明白短路,可以不能指望所有人都明白。

当后来的人维护的时候,很有可能加一个条件,取一个值,放到短路条件的第一个。
如果加的条件是开销很大的调用的话,就悲剧了。

分开写,是为了提醒维护的人,这里的调用顺序是重要的。

看到a||b||c,一般人的反应是,三个条件有一个为true,则返回,而不会想调用顺序的问题。


如果这个真的很重要,为什么不写点注释...


我认为LZ应该是想让程式码自我注释,如果程式码可以自我注释我认为比明确写上注释要好
16 楼 gdpglc 2010-11-16  
zhang_xzhi_xjtu 写道
dwbin 写道
其实觉得代码还是以易读为主,向这样的代码宁肯多写两行,短路的写法是最高效的,但是如果出了问题需要进行单步调试的话还是像楼主那样写起来比较方便。
但是这样写也有个坏处,就是第一个条件被做为了第二个判断的启动条件,不太利于以后的理解,建议还是短路,不过开始的时候增加一个中间变量。
boolean isLoginAgainInMonth=isLoginAgainInMonth(userId);     
return isLoginAgainInMonth||isGardUser(userId);
这样单步调试的时候比较容易控制


这里的纠结在于大家都明白短路,可以不能指望所有人都明白。

当后来的人维护的时候,很有可能加一个条件,取一个值,放到短路条件的第一个。
如果加的条件是开销很大的调用的话,就悲剧了。

分开写,是为了提醒维护的人,这里的调用顺序是重要的。

看到a||b||c,一般人的反应是,三个条件有一个为true,则返回,而不会想调用顺序的问题。


如果这个真的很重要,为什么不写点注释...
15 楼 zhang_xzhi_xjtu 2010-11-16  
dwbin 写道
其实觉得代码还是以易读为主,向这样的代码宁肯多写两行,短路的写法是最高效的,但是如果出了问题需要进行单步调试的话还是像楼主那样写起来比较方便。
但是这样写也有个坏处,就是第一个条件被做为了第二个判断的启动条件,不太利于以后的理解,建议还是短路,不过开始的时候增加一个中间变量。
boolean isLoginAgainInMonth=isLoginAgainInMonth(userId);     
return isLoginAgainInMonth||isGardUser(userId);
这样单步调试的时候比较容易控制


这里的纠结在于大家都明白短路,可以不能指望所有人都明白。

当后来的人维护的时候,很有可能加一个条件,取一个值,放到短路条件的第一个。
如果加的条件是开销很大的调用的话,就悲剧了。

分开写,是为了提醒维护的人,这里的调用顺序是重要的。

看到a||b||c,一般人的反应是,三个条件有一个为true,则返回,而不会想调用顺序的问题。
14 楼 zhang_xzhi_xjtu 2010-11-16  
明天的昨天 写道


这个也考虑过,只是为了方便调试,一般把方法的返回值都独立出来。

我也想知道到底方便在哪里?
楼主的上一个帖子,不是也直接这样了吗?


实践中的重构05_简洁的代码

if (a|| b|| c) {
   return true;
}
return false;


可以改为

return a|| b|| c;


一个是调用方法,一个是变量,不一样。

相关推荐

    方法调用序列追踪工具

    在这个项目中,开发者构建了一个能够跟踪和记录Java应用程序中方法调用顺序的工具。符号执行是一种静态分析技术,它通过创建程序状态的抽象表示来模拟执行,可以用来探索可能的控制流和数据流路径,而无需实际运行...

    重构改善既有代码的设计(PDF)

    8. 整理和重排代码结构:重构时可能需要对类的组织、方法的调用顺序等进行整理,以使代码更加有序。 9. 使用设计模式:重构过程中,合理利用设计模式能够解决很多设计问题。设计模式为代码的组织和结构提供了经过...

    【IT十八掌徐培成】Java基础第20天-03_消息重构——2.zip

    它能帮助提升代码的可读性、可维护性和性能。在Java中,重构可能包括提取方法、重命名变量、移动函数或字段等操作。 4. **重构的意义**:重构可以降低复杂度,提高代码的可测试性,使得团队协作更加高效。当系统...

    《重构——改善既有代码的设计》第一个案例代码

    《重构——改善既有代码的设计》是Martin Fowler的经典著作,它为软件开发人员提供了一套系统化的重构技术,旨在提升代码...通过这样的实践,我们可以学习到如何在实际项目中运用重构技术,提升软件开发的质量和效率。

    重构-改善既有代码的设计

    1. 保证软件的外部行为不变:在重构过程中,最核心的要点就是保证软件的现有功能和性能不受影响。这意味着在对代码进行修改时,不能改变软件的输入和输出结果。 2. 小步快跑:重构应该采取小步骤进行,每个小的修改...

    eclipse 重构

    标题 "Eclipse 重构" 指向的是在Eclipse集成开发环境中进行代码重构的实践与技巧。Eclipse是一款强大的开源Java IDE,它提供了丰富的重构工具,帮助开发者提高代码质量,提升软件设计,并且在不改变代码外在行为的...

    软件工程与软件系统可重构性评估.pptx

    - **模型验证**:构建软件的抽象模型,通过形式化的方法验证其正确性和性能。 #### 第2章 软件重构技术 **重构技术概述** 软件重构是在不改变软件功能的前提下,对其结构进行改善的过程。通过重构可以提高代码的...

    构建,重构二叉树并输出

    在IT领域,特别是数据结构和算法的学习中,构建和重构二叉树是一项基本技能。本文将深入探讨如何根据前序序列和中序序列构建二叉树,并使用MFC(Microsoft Foundation Classes)框架来实现图形化输出。对于二叉树的...

    TemplateMethod 模式与代码重构

    TemplateMethod(模板方法)设计模式是一种行为设计模式,它在面向对象编程中扮演着重要的角色。...通过查看这些文件,我们可以更深入地了解如何在实践中应用模板方法模式以及如何结合代码重构优化代码结构。

    读《重构》的小量笔记

    《重构》是软件开发领域的一本经典著作,由Martin Fowler所著,主要探讨如何通过改进代码结构来提高软件质量,而...在实践中,我们需要遵循一些基本原则和模式,同时保持对系统性能的关注,确保重构工作有效且高效。

    专题资料(2021-2022年)PLSQL程序优化和性能分析方法要点.doc

    【PLSQL程序优化和性能分析方法要点】 PLSQL程序优化主要关注如何提高代码效率,减少系统资源的消耗,特别是CPU、内存和IO的利用率。性能问题是程序设计不合理、不规范的结果,性能测试包括压力测试和性能测试两...

    代码阅读方法与实践

    本文将围绕“代码阅读方法与实践”这一主题,详细探讨如何有效地进行代码阅读,提高开发效率和代码质量。 首先,我们要明白代码阅读的目的。它不仅是为了理解代码的功能,更是为了理解代码的设计思想、架构以及实现...

    重构-改善既有代码的设计(中文版)

    ### 重构——改善既有代码的设计 #### 知识点概览 **重构**是指在不改变软件外部行为的前提下,对代码进行结构上的调整,以提高其可读性、可...通过不断实践和学习,重构可以成为软件开发过程中不可或缺的一部分。

    findbugs报告问题含义

    `FindBugs` 是一款静态代码分析工具,用于检测 Java 代码中的潜在错误和不良实践。这份报告列举了一些常见的 `FindBugs` 报告问题,让我们逐一解析这些错误并探讨如何解决它们。 1. **IMSE_DONT_CATCH_IMSE**:捕获...

    防止多个地方调用相同一个过程

    例如,如果有一个计算平方的函数,在工程1.vbp和工程1.vbw文件中定义的窗体和工程对象可能都在调用这个函数,而不是各自实现这个功能,从而避免了重复代码。 3. **互斥(Mutex)**: 在多线程或并发环境中,当多个...

    微服务架构——应用产品的微服务化之路—拆分与重构 共38页.pdf

    - **场景A: 交易撮合**:由于需要保证严格的顺序执行和数据一致性,因此不建议在这个场景中使用微服务。可以通过增强单个引擎的能力来满足性能要求。 - **场景B: 证券经纪**:对于需要强一致性的业务,可以采用TCC...

    16 M文件和matlab程序性能分析.zip

    在MATLAB环境中,M文件是实现算法和程序的主要方式,它们是文本文件,包含了MATLAB可执行的命令和函数。本资源"16 M文件和matlab程序性能分析.zip"显然是一个关于如何优化和分析MATLAB程序性能的教程或资料集合。...

    JAVA性能测试与调优案例

    通过对实际案例的分析与实践,旨在帮助开发人员提升系统的整体性能。 #### 二、JDK优化 **1. 如何修改默认Java调用的JVM.dll版本** Java虚拟机(JVM)有两种版本:client和server。其中,client版本适用于桌面应用...

Global site tag (gtag.js) - Google Analytics