PHP 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数

思维导图 点击下图,可以看大图。
介绍

我把我比较喜欢的和比较关注的地方写下来和大家分享。上次我写了篇《php 跟老大的对话》。还是有很多疑问,这书帮了我不少的忙。

如果你比较繁忙,或者懒得看文字,建议你直接看截图,也会有很大的收获的。你可以通过比较截图中的代码就能知道孰优孰劣了。

代码部分我为什么用图呢?因为我经常用手机看代码,博客园的代码在手机里乱七八糟的,还是看图比较舒服。

专业术语

我们毕竟是用英文字母编码,所以用一些英语单词,更能显示出我们的专业性。以下的英文单词,你如果掌握了,与其他coder交流的时候会更直接,更专业。——臭显摆一下吧,呵呵。
“*”表示文中经常提到的

inline:内联
function:函数
*method:方法
finely grained:细粒度的
rename:重命名
query:查询
temp:临时(temporary)——一般指临时变量
*extract:提取——我个人更喜欢翻译成“提炼”
*duplicate:复制
split:剖解
variable:变量
factor:因素,因子

重构原则

一、何谓重构?
  名词形式:对软件内部结构的一种调整,目的是在不改变软件之可察行为前提下,提高其可理解型性,降低其修改成本。
  动词形式:使用一系列重构准则,在不改变软件之可察行为前提下,调整其结构。

二、为何重构 ?
  1、经常重构可以让代码维持该有的形态。
  2、让代码找到合适的位置。
  3、让软件更易理解。
  4、可以找到bug。
  5、提高我们的编码速度。
三、重构的难题
  1、修改接口命名
    如果你的类中的方法是public,那么你在rename的时候,冒着很大的风险,你不知道到底有哪些模块在调用你的这个方法(我们经常的做法是在整个项目下做grep操作,然后逐一看各个模块的调用和逻辑)。——所以我们在编写类的时候不管是属性还是方法尽量做到private,避免接口开放。

  2、何时不该重构
    (1)重写所有代码,而且现有代码实在太混乱,重构还不如重写。
    (2)项目临近结束的时候,应该避免重构。我们可以把重构放到二期去解决。

代码的坏味道

一、Duplicate Code
  1、同一个类,两个方法含有相同表达式。
    解决方法:你可以Extract Method提炼重复代码,然后让这两个方法都调用这个Extract Method。
2、两个类,有相似的方法。
    解决方法:(1)把两个类的方法提出来,共同构造一个父类。
         (2)把其中一个类的方法删除,调用另一个类的方法。
二、Long Method
  1、短函数:代码阅读费点力气,因为我们必须经常转换上下文去看看子程序做了什么。但是让small method容易理解的真正关键在于一个好的名字。读者可以通过名字了解函数的作用,根本不必去看其中写了些什么。——早期的编程语言中,调用方法需要额外开销,这使得coder不愿意使用small method。但是现代的OO语言几乎已经完全免除了process内的额外开销(函数调用)。

  2、注释地方提炼信号:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立函数中,并以其用途命名。可以对一组或甚至短短一行代码做这件事。——只要函数名称能够解释其用户,我们也该毫不犹豫地那么做。

"函数"理解为”做什么“或”如何做“

  3、条件式和循环常常也是提炼信号。

  4、《代码整洁之道》的一个例子。我们可以想想!

三、Large Class

  1、Class内数个属性变量有相同前缀或者字尾,可使用Extract Class。

  2、Class内并非大多数变量使用属性变量,可使用Extract Class。
  
  3、有太多代码,可Extract Class。

四、Long Parameter
  做成Introduce Parameter Object。——这个我不太赞同,因为我在使用别人方法的时候,我很少去看代码实践,更不要说去看里面都用到了对象的那些属性或者方法,取我想要的数据了。

五、Switch Statements
  1、少用switch语句。——问题在于duplication。添加新case的时候,你必须找到所有case并修改它们。
  
  2、用多态来替换它。做法:1.将switch进行Extract Method;2.MoveMethod把case里的实践代码放到多态性的class里。

六、 Comments
  试试用Extract Method,如果还不行,那你试试Rename Method。

当你感觉需要撰写注释,请先尝试重构,试着让所有注释变得多余。

  注释一般用于将来的打算,还可以用于你并无十足把握的区域(为什么做某事)。

重新组织你的函数

  Long Method往往包含太多信息,这些信息又被错综复杂的逻辑掩盖,不易鉴别。

一、Extract Method

状况:我看见一个过长的函数或者需要一段注释才能让人理解用途的代码,那么将这段代码放进一个独立函数中,并让函数名称解释改函数的用途。

动机:

简短而有良好命名的函数:——finely grained

  1、复用机会大。

  2、函数读起来像读一系列comments。

  3、函数覆写容易。

重点:函数长度关键在于函数名称和函数本体之间的语义距离。如果提炼动作可以强化代码的清晰度,那么就去做。

作法:

  1、创建新函数,根据函数的意图命名——以它“做什么”命名,而不是以它“怎样做”命名。

    =》 即使Extract Function 非常简单,例如只是消息或函数调用,只要新Function能够以更好方式昭示代码意图,你也应该提炼它。但如果你想不出更有意义的名称,就别动它。

  2、将Extract的代码从Source Function 中Move到New Function中。

二、Inline Method

  Method Body与Method Name一样清晰易懂的时候,请Inline Method。

三、Inline Temp

一个临时变量,只被一个简单表达式赋值一次,而且赋值完也只使用了一次。——请Inline Temp

四、Replace Temp with Query

如果一个Temp变量,保存一个表达式,将这个表达式Extract Method。——这就是所谓的查询式,query

动机:

  1、局部变量会使代码难以提炼。

  2、临时变量会驱使你写出更长的代码。如果改成query method,那么class下的method,都可以获得这份信息。——将编写出更清晰的代码。

  3、Replace Temp with Query往往是你运用Extract Method之前必不可少的步骤。

作法:

  1、找出只被赋值一次的临时变量。

    =>  如果临时变量赋值超过一次,考虑使用Split Temporary Variable将它分割成多个变量。

  2、对Temp Variable赋值的右侧部分,Extract到一个独立函数中。

   =>  将Method声明为private,日后如果有其他class用的时候再放开它(public或protected)。

  

如果代码组织良好,那么你往往能发现更有效的优化方案。————如果性能真的很糟糕,那么放回去也很容易。

五、Introduce Explaining Variable

将复杂表达式中(或其中一部分)的结果放进一个临时变量,以此变量名称来解释表达式用途。

动机:

  表达式复杂而且难以阅读。在这种情况下,临时变量可以帮助你将表达式分解为比较容易管理的形式。

  

六、Split Temporator Variable

某个临时变量被赋值超过一次,它既不是循环变量,也不是集合变量。那么针对每次赋值,创造一个独立的,对应的临时变量。

动机:

  1、如果临时变量承担多个责任,它就应该被替换为多个临时变量。每个变量只承担一个责任。

  2、同一个临时变量承担两件不同的事情,会令review变得糊涂。

六、Remove Assignments To Parameters

如果你的代码对参数进行赋值,那么以一个临时变量取代该参数的位置

七、Replace Method with Method Object

大型函数对局部变量的使用无法采用Extract Method。那么将这个Method放进一个单独对象中,如此一来,让局部变量成为对象的filed,然后在同一个对象中将大型函数分解为数个小型Method。

动机:

  1、将相对独立的代码从大型Method中Extract出来,就可以大大提高代码的可读性。

  2、一个Method中,局部变量泛滥成灾,分解这个函数将会非常困难。

  3、Replace Method with Method Object 会将所有局部变量变成对象的值域。然后对这个新对象进行Extract Method了。

八、Substitute Algorithm

如果你想把某个算法替换为另一个更清晰的算法,那么将Method Body替换为另一个算法。——就是直接修改原来的Method Body。

动机:随着对问题有了更多的了解,你发现一件事可以有更清晰的方式,就应该以较清晰的方式取代复杂方式。

总结

这只是本书的一部分内容,我知道会有很多的coder应该有不同的观点,我自己也是,有的很赞同,有的我也是不太赞同的。所以要“则其善之而从之,其不善之而改之”。

欢迎大家发表下自己的看法。

时间: 2012-04-08

PHP代码维护,重构变困难的4种原因分析

本文分析讲述了PHP代码维护,重构变困难的4种原因.分享给大家供大家参考,具体如下: 代码维护,重构是件很令人不爽的一件事.以下几种情况,会让代码维护和重构变得很困难. 1. 项目开始时,大家规定好一些代码规范,在一定的规范下进行开发,但是人的思想是不一样的,也就是说每个功能不同的人实现的逻辑可能会有这样那样的不同,导致了一些人不愿意去看别人代码,要改别人代码,首先要了解这个人当时是怎么想的,他的逻辑是怎么样的.所以有很多人的想法是有那看别人代码的时间,我就重新做好了.这种想法不要有,看别人代码

用PHP实现维护文件代码

PHP有很多与文件系统相关的函数,不仅可以帮助你打开文件,还可以显示目录内容.移动文件等.很多人甚至使用PHP写出了基于Web的文件管理器. 首先需要提醒一些关于文件路径的东西:在Windows你可以在文件路径中使用斜线"/"或反斜线"\",而其他操作系统仅使用"/".出于兼容性考虑,以下实例使用"/"的表示方法: 下面的简单脚本显示了一个基本的目录列表.注释在代码中并解释了每一步: <? /* 在变量 $dir_nam

五款PHP代码重构工具推荐

在软件工程学里,重构代码一词通常是指在不改变代码的外部行为情况下而修改源代码.软件重构需要借助工具完成,而重构工具能够修改代码同时修改所有引用该代码的地方.本文收集了五款出色的PHP代码重构工具,以帮助你完善更加优秀的项目. 1. Rephactor Rephactor是一款命令行重构工具,这是一款自动化工具,允许开发者以一种简洁的方式在不同的代码库中修改源码. 主要功能: 保证重构的可逆性-- 一旦发现问题,代码是可逆的,可以回溯到前一个版本. 查找替换功能-- 普通查找替换,方法重命名,类重

PHP 杂谈《重构-改善既有代码的设计》之五 简化函数调用

思维导图 介绍 前几篇系列文章,我比较关注的是<PHP 杂谈<重构-改善既有代码的设计>之一 重新组织你的函数>,但是我觉得我还是没有说清楚,我自己也有很多不理解的地方,而且这篇是我的第一篇这方面的文章,有很多的纰漏,所以我会经常性的去做修改,如果大家有好的意见不妨告知一.二. 今天谈得是"接口",此接口非"Interface",而是一个统称.我们一般可以把供别人使用的函数或者url(一般是用于提供数据)叫接口.--可能还有别的意思,毕竟我现

rephactor 优秀的PHP的重构工具

PHP框架可以是单一入口,完全面向对象的,完全基于类的MVC模式.但是,我们面对大量的旧的代码,或即便是新的代码,也不尽然完全符合面向对象的原则,符合设计模式.小的应用无妨.但如果面对大型应用,则必然是一个不小的疼痛!! 怎么办?很多人总会面临这一切,PHP代码需要重构.(当然,你要是能明白我所说的这一切,那你肯定是看过<重构--改善既有代码的设计>这一本书) 看看这个链接:http://zh-cn.w3support.net/index.php?db=so&id=100876 我们就

使用PHP维护文件系统

PHP有很多与文件系统相关的函数,不仅可以帮助你打开文件,还可以显示目录内容.移动文件等.很多人甚至使用PHP写出了基于Web的文件管理器. 首先需要提醒一些关于文件路径的东西:在Windows你可以在文件路径中使用斜线"/"或反斜线"\",而其他操作系统仅使用"/".出于兼容性考虑,以下实例使用"/"的表示方法: 下面的简单脚本显示了一个基本的目录列表.注释在代码中并解释了每一步: <? /* 在变量 $dir_nam

PHP 杂谈《重构-改善既有代码的设计》之四 简化条件表达式

思维导图 点击下图,查看大图. 介绍 条件逻辑有可能十分复杂,因此本章提供一些重构的手法,专门用来简化它们. 全文简述(你可直接跳过下面的内容) 核心重构:Decompose Conditional--分离"转辙逻辑"(switching logic)和"操作细节"(details)分离. 多处测试有相同结果:Consolidate Conditional Expresssion 条件代码中去掉重复成分:Consolidate Duplicate 标识特殊情况:Re

PHP 杂谈《重构-改善既有代码的设计》之二 对象之间搬移特性

思维导图 索引: Ø Move Method(搬移函数) Ø Move Field (搬移值域) Ø Extract Class (提炼类) Ø Inline Class (将类内联化,就是把当前的类合并到其他类中) Ø Hide Delegate (隐藏委托关系) Ø Remove Middle Man ( 移除中间人) Ø Introduce Foreign Method (引入外加函数) Ø Introduce Local Extension (引入本地扩展) 介绍 承接上文PHP 杂谈<

PHP 杂谈《重构-改善既有代码的设计》之三 重新组织数据

思维导图 介绍 承接上文的PHP 杂谈<重构-改善既有代码的设计>之 重新组织你的函数继续重构方面的内容. 这章主要针对数据的重构. 1.争论的声音--直接访问Field还是通过函数(Accessor)访问Field 2.修改Array为Object:当你看到一个Array很像一个数据结构,你可以使用Replace Array with Object,把Array变成一个对象.--数据结构更清晰. 专业术语 accessor:访问者,存储器--在本文翻译为"函数" dumb

Swift 中如何使用 Option Pattern 改善可选项的 API 设计

SwiftUI 中提供了很多"新颖"的 API 设计思路和 Swift 的使用方式,我们可以进行借鉴,并反过来使用到普通的 Swift 代码中.PreferenceKey 的处理方式就是其中之一:它通过 protocol 的方式,为子 view 们提供了一套模式,让它们能将自定义值以类型安全的方式,向上传到父 view 去.如果有机会,我会再专门介绍 PreferenceKey,但这种设计的模式其实和 UI 无关,在一般的 Swift 里,我们也能使用这种方法来改善 API 设计. 在

js获取鼠标位置杂谈附多浏览器兼容代码

其实获得鼠标位置就是一句话的事情,但是要是不了解浏览器间的区别,整个过程就会让人很郁闷,区别在与: 1.IE下获得x和y轴的距离分别用event.x和event.y: FF下获得x和y轴的距离分别用event.pageX和event.pageY: 2.IE下用此方法获得的位置是不算滚动条滚过的位置的,即它只会算到浏览器边缘: FF则会算进滚过去的那些位置: (这个地方很让我崩溃,为这么点小区别我把所有用到的程序全部重写了一遍,囧死--) 解决1的办法,无非是判断一下浏览器,然后用什么方式: 解决

PHP代码重构方法漫谈

本文实例分析了PHP代码重构方法.分享给大家供大家参考,具体如下: 随着 PHP 从一种简单的脚本语言转变为一种成熟的编程语言,一个典型的 PHP 应用程序的代码库的复杂性也随之增大.为了控制对这些应用程序的支持和维护,我们可以使用各种测试工具来自动化该流程.其中一种是单元测试,它允许您直接测试所编写代码的正确性.然而,通常遗留代码库是不适合进行这种测试的.本文将介绍对包含常见问题的 PHP 代码的重构策略,以便简化使用流行的单元测试工具进行测试的过程,同时减少改进代码库的依赖性. 简介 回顾

代码整洁之道(重构)

前言 之前也介绍过我们团队的前端项目从零开始经历8个月迭代业务代码10万行(仅为产品长期规划需求的20%),至今仍然在不断迭代的过程. 团队成员除了设计好的架构来管理这种复杂度极高的前端应用,还开始补充设计模式以及重构方面的知识,目的是为了让项目代码在不断迭代的过程中优化项目代码,保持代码的新鲜度,鲁棒性,可维护性- 让后续加入的团队新人也可以快速加入我们的产品开发中 PS:  不管对于何种语言,重构都是软件开发过程中不可或缺的一部分.如果已经了解重构的基础,可以直接跳往至文章后面的重构案例部分