Michael Feathers是Object Mentor International公司的技术顾问。他的工作不仅是技术开发,他还参与对世界各地技术团队进行培训、指导等工作。他曾开发了将JUnit迁移到C++的CppUnit的初始部分,还有FitCpp——一个C++版的FIT基础测试框架。他是《Working Effectively with Legacy Code》一书的作者。

  条件控制是编程中与生俱来的一种结构,但对于我来说,除了给我带来麻烦外,没有发现任何的用处。一次又一次,我不断发现,越少的if语句,越少的switch语句,越少的循环,就会是越好的代码。通常这其中的原因是程序员用编程语言实现了更好的抽象归纳。他们并不是有意识的避免使用控制结构。但他们确实做到了这些。

  如果是使用一种面向对象编程语言,我们可以用多态(polymorphism)来代替switch。同样的技巧也能用在if语句上,但如果逻辑太简单,这样做就有点得不偿失。当使用一种有函数式特征的编程语言时,大部分的循环执行任务我们都可以用map,filter,fold等实现。控制结构最终从代码中消失,这是对代码大有好处的事。

  条件控制结构的问题是,它很容易导致你把代码修改的乱七八糟。让我们看看下面一个简单的if语句:

 if ...
...
else
...
end

  代码中所有打省略号的地方都是你可以不断添加代码的地方。这些地方可以访问if外面的变量。这很容易造成高耦合。更糟糕的是,人们会习惯性的在条件控制里嵌套条件。我见过的最糟糕的代码,里面的嵌套之深的就像是噩梦里的无底洞。我想,条件控制结构的真正问题所在是,它把各种任务混合到了一起。我相信,你能从某种角度上看出,它是和任务单一编程原则相冲突的。

  我们该怎么做?我们可不可以完全不要控制结构?我想不行,但我们可以做一些实验来看看如何能减少对它们的使用。通常这样做会让我们从中学到一些新技巧,让我们的代码更整洁。

  不久前,我开发了一些Ruby程序,我需要写一个‘take’函数,用它从一个数组里取出一些元素。Ruby里有一些针对Enumerable的这样的函数,但我需要一些特殊的功能。如果我需要的数组的大小超出了目标数组的大小,需要把多余的数组空间都置为0。

  这看起来可以用简单的if语句实现:

 def padded_take ary, n
if n <= ary.length
ary.take(n)
else
ary + [0] * (n - ary.length)
end
end

  让我们认真的看一看这段代码。它没有向我们显示任何填充动作的信息,没有显示数组跟填充的关系。如果认真看,可以看出其中的逻辑,但我们看不出这段代码的意图。

  我们引入一些函数来让这段代码更清楚些,使用guard语句来简化if语句:

 def padded_take ary, n
return ary.take(n) unless needs_padding?(ary, n)
ary + pad(ary, n)
end

  这个短小精悍,但不是更简单——我们可以使用一个null对象来去掉条件语句。空的数组就是很好的null对象。让我们在来一次。

  我们不需要用一个条件语句来计算填充的长度。这个长度我们可以取两个数组中的最大值,如果我们想要的长度超出了数组的长度,填充的长度就是它们的差值:

 pad_length = [0, n - ary.length].max

  有了这个长度,我们可以先填充数组,然后取出我们想要的元素:

 def pad ary, n
pad_length = [0, n - ary.length].max
ary + [0] * pad_length
end

  于是,我们可以这样定义取出动作:

 def padded_take ary, n
pad(ary, n).take(n)
end

  我们通过先进行填充从而避免了使用if语句。当然,有时候填充的是一个空数组。

  我不想去争论这样的写法是否比最初的if-then-else代码更简单,但现在的代码的意图更清晰了,而且我不认为这种策略在这种代码里使用是过度技术化。

  从提取归纳的层面看,代码经过处理后的好处是明显的。当遇到更复杂问题时,它带来的益处将会更明显。

尽量少用if else的更多相关文章

  1. Effective Objective-C 2.0 — 第二条:类的头文件中尽量少引入其他头文件

    第二条:类的头文件中尽量少引入其他头文件 使用向前声明(forward declaring) @class EOCEmployer 1, 将引入头文件的实际尽量延后,只在确有需要时才引入,这样就可以减 ...

  2. 尽量少嵌套无用的div;外部文件尽量使用link而不要使用用@import

    最近的工作又学到了很多东西,在这里记录一下. 1,尽量少嵌套无用的div,这个问题领导很严肃的跟我提过很多次,因为我很喜欢用很多div,而且有很多div都是无存在意义的.后来领导给了我一些资料,我看了 ...

  3. 读书笔记_Effective_C++_条款二十七:尽量少做转型动作

    有关转型的几种做法,已经在早些的博客中写过了.这里先简单回顾一下,再讲一讲effective中对之更深入的阐述. 转型可以按风格可以分成C风格转型和C++风格转型两大类,C风格转型很容易看到,因为我们 ...

  4. 在Spark中尽量少使用GroupByKey函数(转)

    原文链接:在Spark中尽量少使用GroupByKey函数 为什么建议尽量在Spark中少用GroupByKey,让我们看一下使用两种不同的方式去计算单词的个数,第一种方式使用reduceByKey  ...

  5. HashSet非常的消耗空间,TreeSet因为有排序功能,因此资源消耗非常的高,我们应该尽量少使用

    注:HashMap底层也是用数组,HashSet底层实际上也是HashMap,HashSet类中有HashMap属性(我们如何在API中查属性).HashSet实际上为(key.null)类型的Has ...

  6. uvalive 3231 Fair Share 公平分配问题 二分+最大流 右边最多流量的结点流量尽量少。

    /** 题目: uvalive 3231 Fair Share 公平分配问题 链接:https://vjudge.net/problem/UVALive-3231 题意:有m个任务,n个处理器,每个任 ...

  7. ZOJ 3963 Heap Partition set维护。给一个序列,将其划分成尽量少的序列,使每一个序列满足按照顺序构造二叉树,父母的值<=孩子的值。

    Heap Partition Time Limit: Seconds Memory Limit: KB Special Judge A sequence S = {s1, s2, ..., sn} i ...

  8. Effective C++:规定27:尽量少做动作的过渡

    (一个)C风格遗留转换: (T)expression T(expression) (二)C++提供四种新式转型: (1)const_cast<T>(expression):去除表达式的常量 ...

  9. 读书笔记 effective c++ Item 27 尽量少使用转型(casting)

    C++设计的规则是用来保证使类型相关的错误不再可能出现.理论上来说,如果你的程序能够很干净的通过编译,它就不会尝试在任何对象上执行任何不安全或无意义的操作.这个保证很有价值,不要轻易放弃它. 不幸的是 ...

  10. 高并发、海量数据处理尽量少使用using也能提升效率

    请看下面两段: 第一种方式: MemoryStream stream = new MemoryStream(); string text = "aasasdfasdfad;sas;fkqew ...

随机推荐

  1. SQL Server 2012 创建操作员

    数据库可以通知操作员,给操作员发送邮件,就要在SQL Server 的代理中启用数据库邮件,前提是先配置出数据库邮件 右键SQL Server代理,选择属性,按下图设置 保存后,右键操作员,选择新建操 ...

  2. PHP中的Libevent学习

    wangbin@2012,1,3 目录 Libevent在php中的应用学习 1.      Libevent介绍 2.      为什么要学习libevent 3.      Php libeven ...

  3. ios xmpp开发应用后台模式接收聊天信息处理方案

    ios xmpp开发应用后台模式接收聊天信息 最近在使用xmppframwork来实现一个聊天应用,碰到了一个问题,应用进入后台以后,就接收不到消息了: 怎么样才能使应用被切到后台时,应用中的网络连接 ...

  4. 极限编程之TDD

         测试驱动开发(Test-Driven Development,TDD)是通过测试定义所要开发的功能的接口,然后实现功能的开发过程. Test-Driven Development(TDD), ...

  5. apache 配置https

    1.生成密钥# openssl genrsa 1024 > server.key这是用128位rsa算法生成密钥,并保存到server.key文件 2.生成证书请求文件# openssl req ...

  6. Unity破解for mac

    Mac 版本的破解: 1,确定Unity的版本,然后找到对应的crack包,下载.例如 Unity 4.2.0f4 crack包,我已经放到我的资源里了. 2,解压.这里我解压到了桌面上(也就是 /U ...

  7. 解决sqoop需要输入密码的问题

    修改配置文件:vi /etc/sqoop/conf/sqoop-site.xml <property> <name>sqoop.metastore.client.record. ...

  8. spring数据源配置

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-// ...

  9. python发邮件

    # -*- coding:utf- -*- import smtplib,os,sys,string import mimetypes from email import Encoders from ...

  10. C#开发Windows服务 附简单实例实现禁止QQ运行

    本实例主要实现下面三个基本功能 1.C#开发windows服务 2.禁止QQ等程序运行 3.为windows服务创建自动安装程序 下面针对这三个基本功能进行实现 一.C#开发windows服务 Win ...