小酌重构系列[19]——分解大括号

概述

if else, for, while等是程序中最常用的语句,这些语句有一个共同点——它们的逻辑都封装在一对“{}”包围的代码块中。在实现复杂的业务逻辑时,会较多地用到这些语句,可能会形成多层的代码嵌套。代码的嵌套层数越大,代码的缩进层次就越深,这将会降低代码的可读性。
如下图所示,如果我们想理解绿色if代码块的逻辑,需要先了解前3个代码块是如何工作的。

N层嵌套的代码不仅可读性差,也难以维护。当需要变更某一层的代码时,因前后层次的逻辑制约,很容易改出有问题的代码。
本文要讲的“分解大括号”策略正是为了避免这种问题,它也可以使我们的代码变得更加整洁。

示例

重构前

Security类的HasAccess方法为了判断用户是否有权限访问,它用了4层if嵌套。

隐藏代码

public class Security
{
    public ISecurityChecker SecurityChecker { get; set; }

    public Security(ISecurityChecker securityChecker)
    {
        SecurityChecker = securityChecker;
    }

    public bool HasAccess(User user, Permission permission, IEnumerable<Permission> exemptions)
    {
        bool hasPermission = false;

        if (user != null)
        {
            if (permission != null)
            {
                if (exemptions.Count() == 0)
                {
                    if (SecurityChecker.CheckPermission(user, permission) || exemptions.Contains(permission))
                    {
                        hasPermission = true;
                    }
                }
            }
        }

        return hasPermission;
    }
}

public interface ISecurityChecker
{
    bool CheckPermission(User user, Permission permission);
}

HasAccess方法的逻辑可以分解为3个步骤:

  1. 当user或permission为null时,返回false
  2. 当参数permission是参数exemptions的一个元素时,返回true
  3. 返回SecurityChecker.CheckPermission(user, permission)调用的结果

下图标示了这3个步骤。

这3个步骤都能决定方法的返回结果,所以没有必要将这些步骤用“{}”嵌套在一起。
我们可以将这3个步骤分离开来,然后指定这3个步骤的执行顺序,并尽快返回结果,就能解除这些嵌套的大括号。

重构后

重构后,我们再次阅读读HasAccess方法,一眼就知道它所表达的逻辑。
这段代码也用到了我后面要讲的一个重构策略——“尽快返回”。

隐藏代码

public class Security
{
    public ISecurityChecker SecurityChecker { get; set; }

    public Security(ISecurityChecker securityChecker)
    {
        SecurityChecker = securityChecker;
    }

    public bool HasAccess(User user, Permission permission, IEnumerable<Permission> exemptions)
    {
        if (user == null || permission == null)
            return false;

        if (exemptions.Contains(permission))
        {
            return true;
        }

        return SecurityChecker.CheckPermission(user, permission);
    }
}

public interface ISecurityChecker
{
    bool CheckPermission(User user, Permission permission);
}

小结

较多层次的嵌套代码,会增加阅读代码的难度。
在编码时,一个方法的嵌套层次应该尽量少。
多少层最合适呢?一般不应该多于一层或两层。

【关注】keepfool

时间: 2024-10-08 00:56:43

小酌重构系列[19]——分解大括号的相关文章

小酌重构系列[4]&mdash;&mdash;分解方法

概述 "分解方法"的思想和前面讲到的"提取方法"."提取方法对象"基本一致.它是将较大个体的方法不断的拆分,让每个"方法"做单一的事情,从而提高每个方法的可读性和可维护性.分解方法可以看做是"提取方法"的递归版本,它是对方法反复提炼的一种重构策略. 分解方法 下图表示了这个重构策略,第1次提炼和第2次提炼都采用了"提取方法"这个策略. 何时分解方法? "分解方法"最终

小酌重构系列[9]&mdash;&mdash;分解依赖

概述 编写单元测试有助于改善代码的质量,在编写单元测试时,某些功能可能依赖了其他代码(比如调用了其他组件).通常我们只想测试这些功能本身,而不想测试它所依赖的代码. 为什么呢?单元测试的目标是验证该功能是否正确,然而功能所依赖的代码是处于功能范围外的,这些代码可能是一些外部的组件,单元测试无法验证这些外部组件的准确性.单元测试因调用"依赖的代码"出错而失败时,会影响测试结果的判断,我们无法确定功能本身是否是正确的.也许功能是正确的,但调用依赖的代码出错时,这个单元测试仍然会被认为是失败

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

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

小酌重构系列[22]——尽快返回

概述 阅读文章时,如果某个段落已经传达了关键信息,我们可能就不会逐字逐句地将文章读完,因为我们已经知道了这篇文章的核心内容.与此类似,如果方法中某些条件判断可以得到结果,我们应该尽快返回该结果. 尽快返回可以带来三个好处 节省阅读代码的时间——如果方法能够尽快返回,后面的代码逻辑可以不必阅读.见下图,如果①已经返回了,就不必阅读②部分的代码 避免执行无效的逻辑——如果方法能够尽快返回,后面的代码逻辑就不会被执行.见下图,如果①已经返回了,②部分的逻辑不会被执行 增强代码的可读性 在分解大括号这篇

小酌重构系列目录汇总

为了方便大家阅读这个系列的文章,我弄了个目录汇总. 方法.字段重构 移动方法 (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) 提取基类.提

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

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

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

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

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

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

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

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