英文原文:Unconditional Programming ]

本文作者介绍

Michael Feathers

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 语句:

  1. if ...
  2. ...
  3. else
  4. ...
  5. end

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

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

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

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

  1. def padded_take ary, n
  2. if n <= ary.length
  3. ary.take (n)
  4. else
  5. ary + [0] * (n - ary.length)
  6. end
  7. end

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

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

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

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

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

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

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

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

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

  1. def padded_take ary, n
  2. pad (ary, n) .take (n)
  3. end

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

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

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

不要if else的编程的更多相关文章

  1. 从直播编程到直播教育:LiveEdu.tv开启多元化的在线学习直播时代

    2015年9月,一个叫Livecoding.tv的网站在互联网上引起了编程界的注意.缘于Pingwest品玩的一位编辑在上网时无意中发现了这个网站,并写了一篇文章<一个比直播睡觉更奇怪的网站:直 ...

  2. JavaScript之父Brendan Eich,Clojure 创建者Rich Hickey,Python创建者Van Rossum等编程大牛对程序员的职业建议

    软件开发是现时很火的职业.据美国劳动局发布的一项统计数据显示,从2014年至2024年,美国就业市场对开发人员的需求量将增长17%,而这个增长率比起所有职业的平均需求量高出了7%.很多人年轻人会选择编 ...

  3. 读书笔记:JavaScript DOM 编程艺术(第二版)

    读完还是能学到很多的基础知识,这里记录下,方便回顾与及时查阅. 内容也有自己的一些补充. JavaScript DOM 编程艺术(第二版) 1.JavaScript简史 JavaScript由Nets ...

  4. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  5. C#异步编程(一)

    异步编程简介 前言 本人学习.Net两年有余,是第一次写博客,虽然写的很认真,当毕竟是第一次,肯定会有很多不足之处, 希望大家照顾照顾新人,有错误之处可以指出来,我会虚心接受的. 何谓异步 与同步相对 ...

  6. UE4新手之编程指南

    虚幻引擎4为程序员提供了两套工具集,可共同使用来加速开发的工作流程. 新的游戏类.Slate和Canvas用户接口元素以及编辑器功能可以使用C++语言来编写,并且在使用Visual Studio 或 ...

  7. C#与C++的发展历程第三 - C#5.0异步编程巅峰

    系列文章目录 1. C#与C++的发展历程第一 - 由C#3.0起 2. C#与C++的发展历程第二 - C#4.0再接再厉 3. C#与C++的发展历程第三 - C#5.0异步编程的巅峰 C#5.0 ...

  8. 猫哥网络编程系列:HTTP PEM 万能调试法

    注:本文内容较长且细节较多,建议先收藏再阅读,原文将在 Github 上维护与更新. 在 HTTP 接口开发与调试过程中,我们经常遇到以下类似的问题: 为什么本地环境接口可以调用成功,但放到手机上就跑 ...

  9. 关于如何提高Web服务端并发效率的异步编程技术

    最近我研究技术的一个重点是java的多线程开发,在我早期学习java的时候,很多书上把java的多线程开发标榜为简单易用,这个简单易用是以C语言作为参照的,不过我也没有使用过C语言开发过多线程,我只知 ...

  10. 异步编程 In .NET

    概述 在之前写的一篇关于async和await的前世今生的文章之后,大家似乎在async和await提高网站处理能力方面还有一些疑问,博客园本身也做了不少的尝试.今天我们再来回答一下这个问题,同时我们 ...

随机推荐

  1. 如何获取jqGrid中选择的行的数据

    原文地址:http://hi.baidu.com/feifan3211/item/c5831f44158761a5df2a9fc1 如何获取jqGrid中选择的行的数据? 下面可以获取选择一行的id, ...

  2. java中集合杂记

    HashSet类按照哈希算法来存取集合中的对象,具有很有的性能.当HashSet向集合中加入一个对象时,会调用对象的hashCode()方法获得哈希码,然后根据这个哈希码进一步计算出对象在集合中的存放 ...

  3. BitBlt介绍

    设备上下文画图有非常多种方法.比如通过创建位图画刷,利用其填充一个区域来实现图像的绘制.此外,还能够使用CDC类的位图函数来输出位图到设备上下文中. BitBlt 用于从原设备中复制位图到目标设备,语 ...

  4. ceph主要数据结构解析3-Ceph_fs.h文件

    (1)集群内部子版本协议类型宏定义:与公共协议保持独立性,以便消息类型和协议升级受影响 #define CEPH_OSDC_PROTOCOL   24 /* server/client */OSD服务 ...

  5. Brunch:快捷的HTML5构建工具

    Brunch,一个超快的HTML5构建工具.它可以(官方介绍): 编译你的脚本,模板,样式,链接它们, 将脚本和模板封装进common.js/AMD模块里,链接脚本和样式, 为链接文件生成源地图,复制 ...

  6. 【Unity3D自我记录】解决NGUI通过问题触发事件点

    在虚拟现实的游戏开发或当,人们功能操作,人们走一下地面行动.但随后点击界面button什么时候,会不会触发click事件.这是通过点.当然,点击界面button当相同的触发点接地运行操作,样也是点透 ...

  7. [转] postgresql常用命令

    PS: 数据库安装后,里面的每个数据库有自己的用户密码,需要dump的时候,指定用户pg_dump -U xxx <数据库>  > 某个地址 最近一直在学习Postgresql,下面 ...

  8. Java基础知识强化81:Math类random()方法之获取任意范围的随机数案例(面试题)

    1. 需求:设计一个方法,可以实现获取任意范围内的随机数 分析:使用方法random()如下: public static double random() 注:Returns a pseudo-ran ...

  9. Unix系统解压tar包时出现@LongLink错误

    Unix系统上使用tar命令解压tar包后,多了一个@LongLink的文件,并且原来的tar包解压后不完整.网上查了下,原因是AIX系统上tar命令自身的一个缺陷.解决办法:把该tar包上传到lin ...

  10. C# SqlHelper

    操作数据库时,经常会把常用的方法封装到一个类中,这里简单写了一个SQLHelper类,供我平时调用. public static class SqlHelper { private static re ...