编写优雅代码的最佳实践

Robert Martin曾说过"在代码阅读中说脏话的频率是衡量代码质量额惟一标准"。同时,代码的写法应当使别人理解它所需的时间最小化,也就是说咱们写的代码是给人看的而不是给机器看的。那么,如何编写优雅代码呢?能够从思想层面和具体技巧层面来优化代码,思想层面指的是遵循面向对象设计原则,本期介绍的是具体技巧。javascript

1. 代码老是越短越好吗?

assert((!(bucket = findBucket(key))) || !bucket.isOccupied());

上面这行代码虽然比较短,可是难以阅读。为了更好地阅读,咱们作以下修改:java

bucket = findBucket(key); if(bucket != null){ assert(!bucket.isOccupied()); }

减小代码行数是一个好目标,可是让阅读代码的事件最小化是个更好的目标。算法

2. 给重要语句添加注释

// Fast version of "hash = (65599*hash) + c"
hash = (hash << 6) + (hash << 16) - hash + c

上面这行代码若是没有添加注释,咱们根本不知道是什么意思,可是有了这行注释,咱们就知道经过移位操做来提高性能。服务器

3. tmp的使用

tmp是咱们常常用的,譬如说两个变量置换,都已变成约定俗成了。微信

tmp = right; right = left; left = tmp;
String tmp = user.getName(); tmp += " " + user.getPhoneNumber(); tmp += " " + user.getEmail(); template.set("user_info",tmp);

4.i,j,k,iter,it:只用作索引或者循环迭代

i,j,k,iter,it被用作索引或者循环迭代已成为业界规范了(i是index的缩写),例如:函数

for(int i=0;i<100;i++){ for(int j=0;j<100;j++){ ...... } } Iterator<String> iter = list.iterator(); while(iter.hasNext()){ ...... }

若是咱们在其余地方使用i,j,k,那么就会增长阅读者的时间。性能

5. 附带重要属性

咱们把命名当作一种注释的方式,让它承载更多的信息!优化

6. 名字须要多长?

  • 在小的做用域中使用简短的名字
  • 在做用域大的可使用长名字
if(debug){ Map<String,Integer> m = new HashMap<>(); lookUpNamesNumbers(m); print(m); }

7. 不要使用容易误解的名字

results = Database.all_objects.filter("year<=2011")

上面这行代码结果如今包含哪些信息?filter是把年份小于等于2011年的数据过滤掉?仍是保留?this

8. 推荐用min和max来表示极限

MAX_ITEMS_IN_CART = 10; if (shoppingCart.numOfItems()> MAX_ITEMS_IN_CART){ error("Too many items in cart"); }

9. 推荐用begin和end来表示包含/排除范围

begin表示包含,end表示排除,在Java中典型的例子就是String.substring()spa

String s = "Hello world"; s.substring(2,5);-> "llo"

10.与使用者的指望相匹配

通常来讲,getter方法就是获取一个字段的值,用户期待的是轻量级的方法,若是你要是在其中作了太多的计算,就应该考虑更名。

public double getMeanPrice(){ //遍历全部条目计算总价,而后计算平均价格 } public double computeMeanPrice(){ //遍历全部条目计算总价,而后计算平均价格 }

11.不要为那些从代码自己就能快速推断的事实写注释

public  class Account { // Constructor public Account(){ ... } // Set the profit member to a new value void setProfit(double profit){ ... } // Return the profit from this Account double getProfit(){ ... } };

12. 不要给很差的名字加注释--应该把名字改好

// Releases the handle for this key.This doesn't modify the actual registry.
void deleteRegistry(RegistryKey key)

乍一看咱们会误认为这是一个删除注册表的函数,但是注释里澄清它不就改动真正的注册表。所以,咱们能够用一个更加自我说明的名字,例如:

void releaseRegistryHandle(registryKey key);

13.为代码中的瑕疵写注释

// TODO:采用更快算法或者当代码没有完成时 // TODO(dustin):处理除JPEG之外的图像格式

14.为常量写注释

// users thought 0.72 gave the best size/quality tradeoff
image_quality = 0.72; // as long as it's >= 2*num_processors,that's good enough NUM_THREADS = 8; // impose a reasonable limit - no human can read that much anywhere const int MAX_RSS_SUBSCRIPTIONS = 1000;

15. 站在读者的角度写注释

struct Recoder {
    vector<float> data; ... void clear(){ // 每一个人读到这里都会问,为啥不直接调用data.clear() vector<float>().swap(data); } }

若是有一个好的注释能够解答读者的疑问,将上述进行以下修改:强制Vector真正地把内存归还给内存分配器,详情请查阅STL swap trick。

16. 公布可能的陷阱

void sendMail(String to,String subject,String body);

这个函数因为须要调用外部服务器发送邮件,可能会很耗时,有可能致使使用者的线程挂起。须要将这段描述放到注释中。

17. 条件语句中参数的顺序

通常原则:将变量放在左边,常量放在右边。更宽泛地说,将比较稳定的变量放在右边,变化较大的放在左边。如 if ( length >= 10) 而不是 if ( 10 <= length)。可是,在非“大小”比较的状况下,上面的原则彷佛不起做用,例如验证一个请求参数是否为某个特定值:if ( request.getParameterValue("name")).equals("Brandon")),此时将常量"Brandon"能够避免出现空指针的状况(上行的参数没有name或者值为空)。

18. if/else语句块的顺序

if/else书写规范:首先处理正逻辑而不是负逻辑,例如 if(ok),而不是if(!ok);其次处理掉简单的状况,这有利于让if和else处理代码在同一个屏幕内可见。

19. 经过提前返回减小嵌套

使用提早返回的机制,能够把函数的嵌套层级变浅。举个栗子,没有使用提早返回的代码:

static bool checkUserAuthority() { bool a, b, c, d, e; if (a) { if (b) { if (c) { if (d) { if (e) { return true; } } } } } return false; }

使用了提早返回的代码:

static bool checkUserAuthority() { bool a, b, c, d, e; if (!a) return false; if (!b) return false; if (!c) return false; if (!d) return false; if (!e) return false; return true; }

20. 经过 "总结变量" 增长可读性

if(request.user.id == document.owner_id){ // user can edit this document ... } if(request.user.id != document.owner_id){ // document is read-only... }

经过观察,咱们提取一个变量final boolean user_owns_document=(request.user.id == document.owner_id),接着代码就能够修改为:

if(user_owns_document){ // user can edit this document ... } if(!user_owns_document){ // document is read-only... }

21. 减小控制流变量

在while、for等循环语句中,咱们一般使用自定义的bool变量,来控制流转。

boolean done = false; while(/* condition */ && !done){ ... if(...){ done = true; continue; } }

以咱们的经验,"控制流变量" 能够经过优化程序结构、逻辑来消除。

while(/* condition */){ ... if(...){ break; } }

22. 缩小变量的做用域

void foo(){ int i = 7; if(someCondition){ // i is used only within this block } } void foo(){ if(someCondition){ int i = 7; // i is used only within this block } }

23. 不要为了共享而把变量设置为类的字段

public class LargeClass{ String s; void method1(){ s = ... method2(); } void method2(){ //使用s } }

经过参数传递来实现数据共享

public class LargeClass{ void method1(){ String s = ... method2(s); } void method2(String s){ //使用s } }

24. 不要把全部变量都定义在开头

把全部变量定义在开头是C语言的风格,面向对象语言习惯将变量定义在离它开始使用的地方。

public void foo(){ boolean debug = false; String[] pvs; String pn; String pv; ... }

除了上述建议以外,咱们还能够参考阿里Java规范,关注微信号:"木可大大",发送"阿里Java规范"便可得到相关资料。

相关文章
相关标签/搜索