小酌重构系列[5]——为布尔方法命名

概述

当一个方法包含大量的布尔参数时,方法是很脆弱的,由此还可能会产生两个问题:

1. 方法不容易被理解
2. 给方法的使用者造成一定困扰,可能会产生一些预期之外的结果。

本文要介绍的重构策略“为布尔方法命名”,可以有效地避开这两个问题。

为布尔方法命名

大量布尔参数带来的问题

下图中的SomeClass的SomeMethod包含3个布尔参数,如果没有注释,调用者根本不知道3个布尔参数所代表的含义。

即使为这个方法提供了详细的注释,调用者也很容易在调用时出错。
调用者一不小心写错了其中一个参数,就可能产生非预期的行为。

在实际的业务处理中,也许你仅用这3个参数处理2~3个业务Case。
但这个方法有3个布尔参数,相当于为调用者提供了8种调用Case!
难道你的程序需要提供8种Case吗?完全没有必要,因为其他几种Case可能根本不会发生!

var obj = new SomeClass();
obj.SomeMethod(false, false, false);  // way1
obj.SomeMethod(false, true, false);   // way2
obj.SomeMethod(false, true, true);    // way3
obj.SomeMethod(false, false, true);   // way4
obj.SomeMethod(true, true, true);     // way5
obj.SomeMethod(true, false, false);   // way6
obj.SomeMethod(true, true, false);    // way7
obj.SomeMethod(true, false, true);    // way8

这好比上个世纪生产的电视机遥控器,几十个按键,用户没有说明书根本就不知道怎么用!
对于用户来说,一个遥控器用来调节频道、音量就足够了。
给用户太多地选择反而不是一件好的事情,用户会因太多的选择而徘徊不定,永远不要给用户提供繁杂的使用方式!

重构策略

“为布尔方法命名”这种策略,意在将一些布尔参数提取出来,结合适当的方法命名来取代布尔参数。
接下来,进入我们的示例环节,用实际的示例来说明这个重构策略。

示例

重构前

CreateAccount()方法用于创建账户,它提供了4个参数,后3个参数都是布尔类型的。

public class BankAccount
{
    public void CreateAccount(Customer customer, bool withChecking, bool withSavings, bool withStocks)
    {
        // do work
    }
}

如果你是这个方法的调用者,在你脑袋中或许会蹦出8种调用方式,但你知道该如何调用吗?
我不知道你们的答案是什么,我的答案是:不明确。

重构后

仔细分析“创建银行账户”这项业务,我们发现“创建账户”只会产生两种Case。

1. 验证并创建账户
PS: 你去银行开户时,需要验证你的身份信息。

2. 验证并创建账户,同时存入初始储蓄金额。
PS: 你去银行开户时,当身份信息验证通过后,你可以向银行卡中存入一部分初始金额。

我们提供了2个方法来实现这2种Case,这2个方法使用具有意义的命名,并将原有的CreateAccount()方法用private修饰限制其访问性。

public class BankAccount
{
    public void CreateAccountWithChecking(Customer customer)
    {
        CreateAccount(customer, true, false);
    }

    public void CreateAccountWithCheckingAndSavings(Customer customer)
    {
        CreateAccount(customer, true, true);
    }

    private void CreateAccount(Customer customer, bool withChecking, bool withSavings)
    {
        // do work
    }
}

重构以后,调用者只能使用CreateAccountWithChecking()和CreateAccountWithCheckingAndSavings()方法,这两个方法的名称也很容易让人知道其含义。

时间: 2024-10-21 00:19:29

小酌重构系列[5]——为布尔方法命名的相关文章

小酌重构系列[11]——提取基类、提取子类、合并子类

概述 继承是面向对象中的一个概念,在小酌重构系列[7]--使用委派代替继承这篇文章中,我"父子关系"描述了继承,这是一种比较片面的说法.后来我又在UML类图的6大关系,描述了继承是一种"is a kind of"关系,它更偏向于概念层次,这种解释更契合继承的本质.本篇要讲的3个重构策略提取基类.提取子类.合并子类都是和继承相关的,如果大家对继承的理解已经足够深刻了,这3个策略用起来应该会得心应手. 提取基类 定义:如果有超过一个类有相似的功能,应该提取出一个基类,并

小酌重构系列目录汇总

为了方便大家阅读这个系列的文章,我弄了个目录汇总. 方法.字段重构 移动方法 (2016-04-24) 提取方法.提取方法对象 (2016-04-26) 方法.字段的提升和降低 (2016-05-01) 分解方法 (2016-05-02) 为布尔方法命名 (2016-05-03) 引入对象参数 (2016-05-04) 类.接口重构 使用委派代替继承 (2016-05-07) 提取接口 (2016-05-08) 解除依赖 (2016-05-09) 分离职责 (2016-05-11) 提取基类.提

小酌重构系列[18]——重命名

概述 代码是从命名开始的,我们给类.方法.变量和参数命名,我们也给解决方案.工程.目录命名.在编码时,除了应该遵守编程语言本身的命名规范外,我们应该提供好的命名.好的命名意味着良好的可读性,读你代码的人无需太多的注释,就能通过名称知道它是什么,它能做什么事儿,以及它应该怎么用. 我们命名.命名,不断地命名.既然有这么多命名要做,我们不妨做好他. 关于命名 取名字的成本 取个名字很简单,取个好的名字就不那么容易了.快速随意地取个名字,还不如花点时间取个好名字,因为好名字省下来的时间要比花掉的多.

小酌重构系列[12]——去除上帝类

关于上帝类 神说:"要有光",就有了光.--<圣经>.上帝要是会写程序,他写的类一定是"上帝类".程序员不是上帝,不要妄想成为上帝,但程序员可以写出"上帝类".上帝是唯一的,上帝的光芒照耀人间,上帝是很爱面子的,他知道程序员写了"上帝类",抢了他的风头,于是他降下神罚要惩戒程序员.--既然你写了"上帝类",那么就将你流放到艰难地修改和痛苦的维护的炼狱中,在地狱之火中永久地熬炼. 你看,上帝也是有

小酌重构系列[21]&mdash;&mdash;避免双重否定

避免双重否定 在自然语言中,双重否定表示肯定.但是在程序中,双重否定会降低代码的可读性,使程序不易理解,容易产生错觉.人通常是用"正向思维"去理解一件事情的,使用双重否定的判断,需要开发者以"逆向思维"的方式去理解它的含义.另外,在写程序时,"!"符号很容易被疏忽和遗漏,一不小心则会编写出错误的代码,从而产生bug.所以,在程序中,我们应当尽量避免使用双重否定. 优惠券是否未被使用? 还是以在线商城给用户发放优惠券为例,由于优惠券的初始状态是未被

小酌重构系列[3]&mdash;&mdash;方法、字段的提升和降低

本文要介绍的是4种重构策略,它们分别是提升方法.降低方法.提升字段和降低字段.由于这4种重构策略具有一定的相通性,所以我将它们放到一篇来讲解. 定义 以下是这4种策略的定义 提升方法:当子类的方法描述了相同的行为时,应将这样的方法提升到基类.降低方法:在基类中的行为仅和个别子类相关时,应将这样的行为降低到子类.提升字段:当子类中的字段描述着相同的信息时,应将这样的字段提升到基类.降低字段:当基类中的字段仅仅用于个别子类时,应将这样的字段降低到子类. 以上的定义是较为为枯燥无趣的,各位读者大可不必

小酌重构系列[15]&mdash;&mdash;策略模式代替分支

前言 在一些较为复杂的业务中,客户端需要依据条件,执行相应的行为或算法.在实现这些业务时,我们可能会使用较多的分支语句(switch case或if else语句).使用分支语句,意味着"变化"和"重复",每个分支条件都代表一个变化,每个分支逻辑都是相似行为或算法的重复.当追加新的条件时,我们需要追加分支语句,并追加相应的行为或算法. 上一篇文章"使用多态代替条件判断"中,我们讲到它可以处理这些"变化"和"重复&qu

小酌重构系列[6]&mdash;&mdash;引入参数对象

简述 如果方法有超过3个以上的参数,调用方法时就会显得冗词赘句.这时将多个参数封装成一个对象,调用方法会显得干净整洁.这就是本文要讲的重构策略"引入参数对象"--将方法的参数封为类,并用这个类的对象替换方法中原有的参数. 引入参数对象 下图演示了这个重构策略,OrderSerivce表示订单服务,GetOrders()方法根据一些条件获取订单信息.在重构前,GetOrders()方法看起来像长篇大论,调用时也颇为麻烦:在重构后,GetOrders()方法看起来言简意赅,调用时仅需要传入

小酌重构系列[20]&mdash;&mdash;用条件判断代替异常

概述 异常处理的关键在于何时处理异常以及如何使用异常,有些开发者会觉得try catch的处理和使用难以把握,于是他们秉承着"您可错杀一千,不可放过一个"的想法,给所有的方法添加try catch. 这种方式会对应用程序造成什么影响吗? 从用户角度出发,用户确实难以察觉到什么,应用程序运行正常,使用的体验好像也没什么差别. 从程序角度出发,大量的try catch会降低代码的可读性,只有在异常触发时才会对程序的性能造成较大的影响. 这两种角度有对错吗? 二者都没有错,第一种角度甚至要远