简洁代码之道(1):用多态替代条件语句

前言

本文是我看了 谷歌简洁代码演讲系列 中的 多态和条件语句 的总结。大部分的条件语句是可以用多态代替的,本文将围绕以下问题开展:

  • 为什么要用多态替代条件语句
  • 多态和条件语句的使用场景
  • 如何用多态代替条件语句
  • 在哪里决定要创建什么子类
  • 什么情况下使用多态

为什么要用多态替代条件语句

  • 没有 if 语句的函数更容易阅读。
  • 没有 if 语句的函数更容易测试。
  • 多态的系统更容易维护。

多态和条件语句的使用场景

使用多态的场景

  • 当对象要根据不同的状态表现不同的行为时。
  • 当你需要在很多地方检查相同的条件时。

使用条件语句的场景

  • 主要用于原始对象的比较:<,>,==,!=。
  • 其他

这篇文章主要着重于如何避免 if 语句。

如何避免使用 if 语句

  • 不要返回 null,而是返回一个空的对象,例如说空的链表。
  • 不要返回错误码,而是直接在运行时抛出异常。

如何用多态代替条件语句

如果你有一个条件语句,它根据对象的类型选择不同的行为。那么如何用多态来替代它呢?下面,我们来看一个例子。

条件语句实现的类

Class Update {
    execute() {
        if (FLAG_i18n_ENABLE) {
            //DO A;
        } else {
            //DO B;
        }
    }
    render() {
        if (FLAG_i18n_ENABLE) {
            //render A;
        } else {
            //render B;
        }
    }
}

上面的类根据 FLAG_i18n_ENABLE 来执行不同的操作。可能你以前就是这么写代码的,觉得这样写一点问题都没有。那么,我现在问你,怎么测试上面的类?

下面我们来看看一般的测试方法。

void testExecuteDoA() {
    FLAG_i18n_ENABLE = true;
    Update u = new Update();
    u.execute();
    assertX();
}
void testExecuteDoB() {
    FLAG_i18n_ENABLE = false;
    Update u = new Update();
    u.execute();
    assertX();
}

看完上面的代码,你可能也觉得似曾相识,也觉得没什么问题。

实际上,这样写的类有以下几个问题:

  • 大量的条件语句判断让代码可读性急剧下降。就好像你在高速公路上行驶的时候,开的正开心,前面一个此路不通的公示牌出现,于是你不得不走别的路。看代码也是同样道理,太多的分支语句会让读者晕头转向。
  • 条件语句的存在让测试更加困难。在写测试的时候,你不得不去考虑它的状态码。上面的类只有两个状态,如果有五个状态呢。光是搞清楚状态之间关系就已经够呛了。

多态实现的类

那么,如何用多态来重写上面的类呢?

我们可以分为两步来操作:

  • Update 成为抽象类,方法也抽象。
  • 在子类中的覆盖方法实现条件语句的分支操作。

代码如下

abstract class Update {
    abstract execute();
    abstract render();
}

class I18NUpdate extends Update {
    execute() {
        //Do A;
    }
    render() {
        //render A;
    }
}

class NonI18NUpdate extends Update {
    execute() {
        //Do B;
    }
    render() {
        //render B;
    }
}

测试方法:

void testExecuteDoA() {
    Update u = new I18NUpdate();
    u.execute();
    assertX();
}
void testExecuteDoB() {
    Update u = new NonI18NUpdate();
    u.execute();
    assertX();
}

用多态实现的类,通过继承抽象类,重写抽象方法的方式,避免使用了条件语句。在测试的时候,不需要关心它的状态码,子类本身就已经承载了状态信息。所以你可以看到,在测试的时候,代码非常的清晰易懂。

总结

使用多态实现的类有两个好处:

  • 我们可以通过增加新的子类来添加新的行为,而且不会影响到原来的代码。
  • 不同的操作和概念在不同的类中,容易理解和阅读。

在哪里决定要创建什么子类

咋一看上面的标题有点绕,我们来详细讨论一下问题的来源。上面我们讲了如何多态代替条件语句,但是有一个问题是无法回避的:我们怎么判断要创建哪一种子类?

实际上,我们还是要依靠 FLAG_i18n_ENABLE 来决定实例化哪个子类,也就是说,我们仍然要用到条件语句。看到这里你可能就觉得:说到底还是要靠条件语句,博主忽悠人。此言差矣,这么说,条件语句的使用是必要不充分的。写程序你肯定要用到条件语句,但是用的太多会有前面我们说到的问题。把多态和条件语句结合,才是正道。

我们回到前面的问题,既然条件语句无法避免,因为我们要根据条件决定使用哪个子类。我们先来对类做一个粗略的划分。

  • 负责业务逻辑的类:例如说我们上面的 Update
  • 负责创建类的类:例如说工厂模式中的 Factory

通过上面的划分,我们可以把子类创建交给工厂类。

class Consumer {
    Cosumer(Update u) {...}
}

class Factory {
    Consumer build() {
        Update u = FLAG_i18n_ENABLE
                    ? new I18NUpdate()
                    : new NonI18NUpdate;
        return new Consumer(u);
    }
}

现在我们可以回答上面的问题了:在工厂类中根据条件决定要创建哪个子类。这样处理有以下的好处:

  • 条件语句集中在了一个地方。
  • 没有多余的重复,除了工厂类,其他地方不需要用到条件语句。
  • 分离了职责和全局状态。
  • 相同的代码集中在一个地方。
  • 独立测试变得简单,而且能同时进行。
  • 在子类中可以清楚地看到实现的不同。

什么情况下使用多态

多态虽好,可不要滥用。 – Yonah潇

  • 类的行为根据状态进行变化的时候。
  • 同样的条件语句在多个地方出现的时候。

最后,我只说一句:该用条件语句的时候不要强行用多态。

参考资料
多态和条件语句

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,539评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,594评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,871评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,963评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,984评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,763评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,468评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,850评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,002评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,144评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,823评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,483评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,026评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,150评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,415评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,092评论 2 355

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,105评论 1 32
  • 三重:代码、底层内存、源码 第一阶段:开发常用JavaSE基础、IDE、Maven、Gradle、SVN、Git、...
    guodd369阅读 16,563评论 1 44
  • 所见所闻不可替代 自我改变 勤奋努力
    樊军艺_阅读 95评论 0 0
  • 前言 本文会实现一个基于Vue的九宫格数字游戏,在此之前我们需要有以下的基础 Vue.js Javascript ...
    bb7bb阅读 2,323评论 1 0
  • 如果你不够优秀,人脉是不值钱的,它不是追求来的,而是吸引来的。只有等价的交换,才能得到合理的帮助——虽然听起来很冷...
    智若愚2阅读 129评论 0 0