代码简洁之道读书分享

代码的味道
    命名:命名和设计关联,很难命名,说明可能设计有问题
    重复代码:修改将会导致所有副本都要修改。需要提炼函数
    函数过长:太长很难理解。拆分成多个短小函数,同时讲究命名艺术和函数注释
    参数列表过长:当参数总是同时出现,不放考虑传入对象
    全局数据:你永远也不知道谁修改了它。封住变量预防
    可变数据:作用域越大,风险越大。封装变量,查询和修改分离
    发散式变化:上下文边界不清,变化一点,需要修改多处。提炼函数
    散弹式修改:需要修改的代码四处分散。搬移函数或内联函数
    依恋情节:一个函数引用多个模块功能。最根本的原则是:将总是一起变化的东西放在一块儿
    数据泥团:类中相同的字段,函数相同的参数。考虑提炼一个新类
    基本类型偏执:考虑用对象替代基本类型
    重复的switch:考虑多态
    循环语句:filter 或 map 查找更方便
    冗赘的元素:当类的元素越来越少,那就去掉这个类
    夸夸奇谈的通用:用不上的就搬走
    临时字段:新建一个类,放置各种临时字段
    过程的消息链:最终使用消息的函数,可以考虑提炼
    中间人:适度使用委托,可以考虑用超类
    内幕交易:适度使用继承关系,考虑委托
    过大的类:提炼类,提炼超类
    异曲同工的类:反复提炼,搬移,直到具有相同接口的2个类协议一致
    纯数据类:数据和行为关联到一个类
    被拒绝的馈赠:拒绝使用超类的函数,就改为委托
    注释:当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余

构筑测试体系
    自测试代码的价值:确保所有测试都完全自动化,让它们检查自己的测试结果。一套测试就是一个强大的bug侦测器,能够大大缩减查找bug所需的时间。
    第一个测试:准备测试夹具:测试数据和对象。检查测试夹具是否具有期望的特征(give-when-then);在代码中暂时引入错误;频繁的运行测试
    添加一个测试:编写不算完美的测试经常运行,好多等待一个完美的测试。
    修改测试夹具:如同界面数据变化一样修改测试夹具,检查测试夹具
    探测边界条件:考虑可能出错的边界条件,把测试火力集中在那儿;考虑输入数据是可信数据源还是外部对服务提供,后者必须添加校验和测试。不要因为测试无法捕捉所有的bug就不写测试,因为测试的确可以捕捉到大多数bug。

    基本类型偏执:考虑用对象替代基本类型
    循环语句:filter 或 map 查找更方便

    中间人:适度使用委托,可以考虑用超类
    内幕交易:适度使用继承关系,考虑委托

    修改测试夹具:如同界面数据变化一样修改测试夹具,检查测试夹具

介绍重构名录

重构的记录格式
    名称(name)。要建造一个重构词汇表,列出常见的别名
    速写,更快找到你所需要的重构手法,以代码示例的形式,提供一点图像记忆的线索
    动机(motivation)为你介绍“为什么需要做这个重构”和“什么情况下不该做这个重构”。
    做法(mechanics)简明扼要地一步一步介绍如何进行此重构。
    范例(examples)以一个十分简单的例子说明此重构手法如何运作。

挑选重构的依据
    重构名录,重构手法最值得被记录下来

第一组重构
提炼函数(反向重构:内联函数)
    动机:将意图与实现分开
    做法:
        创造一个新函数,根据这个函数的意图来对它命
        将待提炼的代码从源函数复制到新建的目标函数中。
        仔细检查提炼出的代码,看看其中是否引用了作用域限于源函数、在提炼出的新函数中访问不到的变量。若是,以参数的形式将它们传递给新函数。
        所有变量都处理完之后,编译。
        在源函数中,将被提炼代码段替换为对目标函数的调用。
        测试。
        查看其他代码是否有与被提炼的代码段相同或相似之处。如果有,考虑使用以函数调用取代内联代码令其调用提炼出的新函数。
    范例:无局部变量;有局部变量;对局部变量再赋值
内联函数(反向重构:提炼函数)
    动机:以简短的函数表现动作意图;有一群组织不甚合理的函数;代码中有太多间接层
    做法:
        检查函数,确定它不具多态性。
        找出这个函数的所有调用点。
        将这个函数的所有调用点都替换为函数本体。
        每次替换之后,执行测试。
        删除该函数的定义。
提炼变量(反向重构:内联变量)
    动机:表达式有可能非常复杂而难以阅读
    做法:
        确认要提炼的表达式没有副作用。
        声明一个不可修改的变量,把你想要提炼的表达式复制一份,以该表达式的结果值给这个变量赋值。
        用这个新变量取代原来的表达式。
        测试。
内联变量(反向重构:提炼变量)
    动机:变量能给表达式提供有意义的名字    
    做法:
        检查确认变量赋值语句的右侧表达式没有副作用。
        如果变量没有被声明为不可修改,先将其变为不可修改,并执行测试。
        找到第一处使用该变量的地方,将其替换为直接使用赋值语句的右侧表达式。
        测试。
        重复前面两步,逐一替换其他所有使用该变量的地方。
        删除该变量的声明点和赋值语句。
        测试。
改变函数声明
    动机:函数声明则展现了如何将这些小块组合在一起工作——可以说,它们就是软件系统的关节
    做法:
        简单做法
            如果想要移除一个参数,需要先确定函数体内没有使用该参数。
            修改函数声明,使其成为你期望的状态。
            找出所有使用旧的函数声明的地方,将它们改为使用新的函数声明。
            测试。
        迁移式做法
            如果有必要的话,先对函数体内部加以重构,使后面的提炼步骤易于开展。
            使用提炼函数将函数体提炼成一个新函数。
            如果提炼出的函数需要新增参数,用前面的简单做法添加即可。
            测试。
            对旧函数使用内联函数。
            如果新函数使用了临时的名字,再次使用改变函数声明将其改回原来的名字。
            测试。
封装变量
    动机:
        搬移一处被广泛使用的数据
        封装能提供一个清晰的观测点,可以由此监控数据的变化和使用情况
    做法:
        创建封装函数,在其中访问和更新变量值。
        执行静态检查。
        逐一修改使用该变量的代码,将其改为调用合适的封装函数。每次替换之后,执行测试。
        限制变量的可见性。
        测试。
        如果变量的值是一个记录,考虑使用封装记录
变量改名
    动机:使用范围越广,名字的好坏就越重要
    做法:
        如果变量被广泛使用,考虑运用封装变量将其封装起来。
        找出所有使用该变量的代码,逐一修改。
        给常量改名:复制这个常量;逐一修改引用旧常量的代码,使其引用新的常量
引入参数对象
    动机:它会催生代码中更深层次的改变,将这些数据结构提升为新的抽象概念,可以帮助我更好地理解问题域
    做法:
        如果暂时还没有一个合适的数据结构,就创建一个
        测试。
        使用改变函数声明给原来的函数新增一个参数,类型是新建的数据结构。
        测试。
        调整所有调用者,传入新数据结构的适当实例。每修改一处,执行测试。
        用新数据结构中的每项元素,逐一取代参数列表中与之对应的参数项,然后删除原来的参数。
        测试
函数组合成类
    动机:一组函数形影不离地操作同一块数据
    做法:
        运用封装记录对多个函数共用的数据记录加以封装。
        对于使用该记录结构的每个函数,运用搬移函数将其移入新类。
        用以处理该数据记录的逻辑可以用提炼函数提炼出来,并移入新类
函数组合成变换
    动机:避免计算派生数据的逻辑到处重复
    做法:
        创建一个变换函数,输入参数是需要变换的记录,并直接返回该记录的值。
        挑选一块逻辑,将其主体移入变换函数中,把结果作为字段添加到输出记录中。修改客户端代码,令其使用这个新字段。
        测试。
        针对其他相关的计算逻辑,重复上述步骤。
拆分阶段
    动机:一段代码在同时处理两件不同的事
    做法:
        将第二阶段的代码提炼成独立的函数。
        测试。
        引入一个中转数据结构,将其作为参数添加到提炼出的新函数的参数列表中。
        测试。
        逐一检查提炼出的“第二阶段函数”的每个参数。如果某个参数被第一阶段用到,就将其移入中转数据结构。每次搬移之后都要执行测试。
        对第一阶段的代码运用提炼函数(106),让提炼出的函数返回中转数据结构。
        
重构API
    将查询函数和修改函数分离
        如果某个函数只是提供一个值,没有任何看得到的副作用;也更容易测试。不分离也违反了单一职责原则
    函数参数化
        2个函数逻辑非常相似,传入不同的参数即可合并2个函数
    移除标记参数
        标记参数:影响了函数内部逻辑。实际违反了单一职责原则
    保持对象完整
        传对象的属性值,不如传整个对象
    查询取代参数
        如果参数可以很容易查询得到,就应该在内部查询出来
    参数取代查询
        增加引用透明性
    移除设值函数
        设值函数可能是字段值或客户端改变,想法移除
    工厂函数取代构造函数
        工厂函数更加灵活
    命令取代函数
        将函数封装成一个命令对象,更灵活,更有表达力
    函数取代命令
        简单的函数无需命令对象
处理继承关系
    函数上移
        重复子类上移到超类,避免代码重复
    字段上移
        子类的字段重复就上移到超类
    构造函数本地上移
        子类构造函数有相同的地方,上移到超类,子类使用super重载超类构造
    函数下移
        超类中的某个函数只与一个(或少数几个)子类有关,那么最好将其从超类中挪走,放到真正关心它的子类中去
    字段下移
        如果某个字段只被一个子类(或者一小部分子类)用到,就将其搬移到需要该字段的子类中。
    以子类取代类型码
        枚举,符号,字符串等可以试着用子类实现多态的方式取代
    移除子类
        失去价值的子类应该被移除
    提炼超类
        两个类在做相似的事,可以利用基本的继承机制把它们的相似之处提炼到超类
    折叠继承体系
        超类和子类差别不大时,合并超类和子类
    以委托取代子类
        委托取代子类,可以避免继承一个方向的短板
    以委托取代超类
        超类的函数不适合子类继承,那么就应该使用委托,放弃继承关系        
        
        

 

 

 

 

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

美美的设计

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值