小酌重构系列[23]——封装条件

当条件判断语句较为复杂时(有多个不同的检查项),就像下面这幅图所表示的,会使得代码的可读性会大打折扣,也难以清晰地传达判断意图。

再者,当判断逻辑变更时,我们不得不去修改if语句里面的判断代码。
如果判断写得有问题,则会影响方法的正确性,也会给该方法的单元测试带来一些障碍。

我们可以根据检查项是否需要参数来封装条件,如果检查项不需要参数,则可以将其提取为属性;如果需要参数,则将其提取为方法。
本文要讲的重构策略“封装条件”是基于“提取方法”这个重构策略的。

示例

重构前

这个示例中,PerformCoolFunction()方法的if条件检查项有三个。

public class RemoteControl
{
    private string[] Functions { get; set; }
    private string Name { get; set; }
    private int CreatedYear { get; set; }

    public string PerformCoolFunction(string buttonPressed)
    {
        // Determine if we are controlling some extra function
        // that requires special conditions
        if (Functions.Length > 1 && Name == "RCA" && CreatedYear > DateTime.Now.Year - 2)
            return "doSomething";
    }
}

我们可以不必去理解这个复杂判断的含义,从代码层面看,这个判断有三个问题:

  • 可读性较差,同时也意味着较差的维护性
  • 增加了编写PerformCoolFunction方法单元测试的复杂度,我们不得不为if判断单独追加一个测试case
  • if判断所用的参数和PerformCoolFunction方法无关

重构后

这三个检查项构成的条件表达式可读较差,所以将这个条件表达式提取为一个方法。这样在做条件判断时就变成了if (HasExtraFunctions),代码的可读性由此增强。另外,我们通过提取出来的条件判断方法名称,也可以准确地洞悉判断的意图。

public class RemoteControl
{
    private string[] Functions { get; set; }
    private string Name { get; set; }
    private int CreatedYear { get; set; }

    private bool HasExtraFunctions
    {
        get
        {
            return Functions.Length > 1 && Name == "RCA" &&
                   CreatedYear > DateTime.Now.Year - 2;
        }
    }

    public string PerformCoolFunction(string buttonPressed)
    {
        // Determine if we are controlling some extra function
        // that requires special conditions
        if (HasExtraFunctions)
            return "doSomething";
    }
}

用bool变量代替判断条件

这个示例的判断条件只有3个,而且每个条件的判断语句比较短。
如果遇到一些判断语句较长,且条件个数很多的判断,我们该怎么办?
我的建议是用bool变量代替判断条件。

偷个懒,我们仍然用上面的例子来示范这种方式:

public class RemoteControl
{
    private string[] Functions { get; set; }
    private string Name { get; set; }
    private int CreatedYear { get; set; }

    private bool HasExtraFunctions
    {
        get
        {
            // 前缀c表示condition
            bool cFuncLen = (Functions.Length > 1);
            bool cName = (Name == "RCA");
            bool cCreatedYear = (CreatedYear > DateTime.Now.Year - 2);

            return cFuncLen && cName && cCreatedYear;
        }
    }

    public string PerformCoolFunction(string buttonPressed)
    {
        // Determine if we are controlling some extra function
        // that requires special conditions
        if (HasExtraFunctions)
            return "doSomething";
    }
}
时间: 2024-10-05 06:56:43

小酌重构系列[23]——封装条件的相关文章

小酌重构系列[23]——封装条件

概述 当条件判断语句较为复杂时(有多个不同的检查项),就像下面这幅图所表示的,会使得代码的可读性会大打折扣,也难以清晰地传达判断意图. 再者,当判断逻辑变更时,我们不得不去修改if语句里面的判断代码.如果判断写得有问题,则会影响方法的正确性,也会给该方法的单元测试带来一些障碍. 我们可以根据检查项是否需要参数来封装条件,如果检查项不需要参数,则可以将其提取为属性:如果需要参数,则将其提取为方法.本文要讲的重构策略"封装条件"是基于"提取方法"这个重构策略的. 示例

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

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

小酌重构系列[24]——封装集合

概述 当方法返回类型或属性类型为集合时,有些开发者会千篇一律地使用IList<T>集合.然而IList<T>具有集合的所有操作,这意味着调用者不仅可以读取集合信息,还能够修改集合.业务需求本来只是为调用者提供一个可读的集合,例如数据的查询和展示,但当方法返回IList<T>时,无疑隐式地开放了集合可写的权限.此时,我们无法阻止调用者篡改集合元素. 注意:将属性设定为IList<T>类型时,即使声明为只读的,我们仍然无法避免集合元素的篡改.例如public I

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

概述 继承是面向对象中的一个概念,在小酌重构系列[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) 提取基类.提

小酌重构系列[14]——使用多态代替条件判断

概述 有时候你可能会在条件判断中,根据不同的对象类型(通常是基类的一系列子类,或接口的一系列实现),提供相应的逻辑和算法.当出现大量类型检查和判断时,if else(或switch)语句的体积会比较臃肿,这无疑降低了代码的可读性.另外,if else(或switch)本身就是一个“变化点”,当需要扩展新的对象类型时,我们不得不追加if else(或switch)语句块,以及相应的逻辑,这无疑降低了程序的可扩展性,也违反了面向对象的OCP原则. 基于这种场景,我们可以考虑使用“多态”来代替冗长的条

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

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

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

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

小酌重构系列[12]&mdash;&mdash;去除上帝类

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