Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >何时应该重构代码?如何重构代码?

何时应该重构代码?如何重构代码?

作者头像
kirito-moe
发布于 2021-09-08 02:43:27
发布于 2021-09-08 02:43:27
1.8K00
代码可运行
举报
运行总次数:0
代码可运行

日常工作中,相信大家都见过一些看见就想骂人的代码,那么今天呢,我们就来聊聊何时应该重构代码,以及如何重构代码。文章有点长,但是看完一定会有很多收获哦~

一.重构原则

1.何谓重构

对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。

另一种解释是:使用一系列重构手法,在不改变软件可观察行为的前提下,调整其结构。

重构不止是代码整理,它提供了一种高效且受控的代码整理技术

2.为何重构

改进软件设计:如果没有重构,程序的设计会逐渐变质,重构很像是在整理代码,你所做的就是让所有的东西回到应处的位置上。帮助找到bug:对代码进行重构,可以深入理解代码的作为,在搞清楚程序结构的同时,想不把bug揪出来都难。提高编程速度:良好的设计是快速开发的根本,改善设计、提高可读性,减少错误,这些都是提高质量。

3.何时重构

任何情况下我都反对专门拨出时间进行重构。重构本来就不是一件应该特别拨出时间做的事情,重构应该随时随地的进行

三次法则:第一次做某件事情是只管去做;第二次做类似的事情会产生反感;第三次再做类似的事,你就应该重构

最常见的重构时机是想给软件添加新特性的时候;

重构的另个一原动力是:代码的设计无法帮助我轻松的添加所需要的特性

  • 修改错误的时候
  • review代码的时重构

间接层和重构

计算机科学是这样一门科学:它相信所有的问题都可以通过增加一个间接层来解决。

大多数重构都为程序引入了更多的间接层,重构往往把大型的对象拆成多个小型的对象,把大型的函数拆成多个小型的函数。

但是,间接层是一把双刃剑。每次把一个东西分成两份,你就需要多管理一个东西。如果某个对象委托另一个对象,后者又委托另一个对象,程序会愈加难以阅读。

何时不该重构:有时候既有代码实在太混乱,重构它还不如重新写一个来得简单。

重写而非重构的一个清楚讯号是:现有代码根本不能正常运作。

二.代码的坏味道

1.重复代码

如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们合二为一,程序会变得更好 。

  • 同一个类中有相同的表达式:提炼出重复的代码,然后让两个地方都调用被提炼出来的那一段代码;
  • 两个互为兄弟的子类内含有相同的表达式:提炼出相同代码,将它推入超类内;
  • 两个毫不相干的类中出现:将重复的代码提炼到一个独立的类中。

2.过长的类

拥有短函数的对象活得比较好、比较长。间接层所能带来的全部利益——解释能力、共享能力、选择能力——都是由小型函数支持的。

每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西写进一个独立的函数中。

如何确定提炼哪一段代码?寻找注释是一个很好的技巧。它们通常能指出代码用途和实现手法之间的语义距离。如果代码前方有一行注释,就是提醒你:可以将这段代码替换成一个函数。

条件表达式和循环常常也是提炼的信号。

3.过大的类

如果想利用单个类做太多的事情,其内往往就会出现太多实力变量。

类内如果有太多代码,也是代码重复、混乱病最终走向死亡的源头。

4.过长参数列

太长的参数列难以理解,太多的参数会造成前后不一致、不容易使用,而且一旦你需要更多数据,就不得不修改它。如果将对象传递给函数,大多数修改都将没有必要。

5.发散式变化

如果某个类经常因为不同的原因在不同的方向上发生变化,那么此时也许将这个对象分成两个会更好,这么一来每个对象就可以只因为一种变化而需要修改。

6.散弹式修改

如果每遇到某种变化,你都必须在许多不同的类内做出许多小修改,你所面临的坏味道就是散弹式修改。如果需要修改的代码散布四处,你不但很难找到它们,也很容易忘记某个重要的修改。

把所有需要修改的代码放进同一个类中,如果眼下没有合适的类可以安置这些代码就创造一个。

7.依恋情结

对象技术的要点在于:将数据和对数据的操作行为包装在一起

有一种经典的气味是:函数对某个类的兴趣高过对自己所处类的兴趣。某个函数为了计算某个值,从另一个对象那调用几乎半打的取值函数。

一个函数往往会用到几个类的功能,那么它该置于何处?我们的原则是:判断哪个类拥有最大被此函数使用的数据,然后就把这个函数和那些数据放在一起

8.数据泥团

很多地方看到相同的三四项数据一起出现。这些总是绑在一起出现的数据应该拥有属于他们自己的对象。

首先找到这些数据以字段形式出现的地方,将它们提炼到一个独立的对象中。这么做的直接好处是可以将很多参数列缩短简化函数调用。

9.基本类型偏执

对象的一个极大价值在于:它们模糊了横旦与基本数据和体积较大的类之间的界限

对象技术的新手通常不愿意在小任务上运用小对象——结合数值和比重的money类、有一个起始值和一个结束值组成的range类。将原本单独存在的数值替换成对象,从而走出传统的洞窟,进入炙手可热的对象世界。

10.switch惊悚现身

面向对象的一个最明显的特征是:少用switch语句

一看到switch语句,就应该考虑以多态来替换它。

如果只是在单一函数中有些选择实例,且并不想改动它们,那么多态就有点杀鸡用牛刀了。

11.平行集成体系

每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

消除这种重复性的一般策略是:让一个继承体系的实例引用另一个继承体系的实例

12.冗余类

某个类原本对得起自己的身价,但重构使它身形缩水,不再做那么多工作,这个时候请让这个类庄严赴义吧。

13.夸夸其谈未来性

企图以各种各样的钩子和特殊情况来处理一些非必要的事情,这种怀味道就出现了。如果用到了那就值得去做,如果用不到那就不值得,只会挡你的路,所以把它挪开吧。

如果你的某个抽象类其实没有起到太大的作用,函数上的某些参数未被使用...可以移除它们了。

14.令人迷惑的暂时字段

某个实例变量仅为某种特定的情况而设。这样的代码让人不易理解。在变量未被使用的情况下猜测当初其设置目的,会让你发疯的。

15.过度耦合消息链

如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另个一对象........这就是消息链。采用这种方式,意味着客户代码将与查找过程中的导航结构紧密耦合。一旦对象间的关系发生任何变化,客户端就不得不做出相应的修改。

16.中间人

封装往往伴随着委托。你也许会看到某个类接口有一半的函数都委托给其他类,这样就是过度运用。

17.狎昵关系

有时会看到两个类过于亲密,话费太多的时间去探究彼此的private成分。过分狎昵的类必须拆散,帮它们划清界线,从而减少狎昵行径。

继承往往造成过度亲密,因为子类对超类的了解总是超过后者的主观愿望。如果你觉得该让孩子独立生活了,让他离开继承。

18/异曲同工的类

两个函数做同一件事,却有着不同的签名。

19.不完美的类库

类库函数构造的不够好,又不能修改它们:

  • 如果只想修改类的一两个函数,可以引入外加函数。
  • 如果想要添加一大堆额外行为,建立一个新类包含这些额外行为,让其成为子类。

20.纯稚的数据类

纯稚的数据类是指:它们拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。

  • 封装public字段;
  • 恰当封装容器类字段;
  • 移除不应修改的字段的设置函数;
  • 提炼调用函数以隐藏取值/设值函数;

21.被拒绝的遗赠

子类只运用了父类的一部分函数和数据。为子类建立一个兄弟类,将所有用不到的字段/函数下移至兄弟类,保证超类的纯粹;

22.过多的注释

注释之所以存在是因为代码很糟糕 。注释的最高境界——代码即注释。

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

三.重新组织函数

1.提炼函数

动机:看到一个过长的函数或者一段需要注释才能让人理解用途的代码,将这段代码放一个独立的函数中;

做法:

  • 创造一个新函数,根据这个函数的意图来命名它;

只要新函数的名称能够以更好的方式昭示代码意图,你也应该提炼它。但如果想不到一个更有意义的名称就别动

  • 将提炼的代码从原函数复制到新建的目标函数中;
  • 将被提炼代码段中需要读取的局部变量,当作参数传递给目标函数;
  • 在原函数中,将被提炼代码段替换为目标函数调用。

2.内联函数

一个函数的本体与名称同样清楚易懂。在函数调用点插入函数本体,然后移除该函数。

动机:

  • 一群组织不甚合理的函数。你可以将它们都内联到一个大函数中,再从中提炼出组织合理的小型函数。
  • 使用的太多的间接层,使得系统中的所有函数都似乎只是对另一个函数的简单委托,造成在委托动作之间晕头转向。

做法:

  • 检查函数,确定不具备多态;

如果子类继承了这个函数,就不要将此函数内联,因为子类无法复写一个根本不存在的函数。

  • 找出这个函数的所有调用点;
  • 将这个函数的所有调用点都替换成函数本体。

3.内联临时变量

有一个临时变量,只被一个简单的表达是赋值一次,而它妨碍了其他重构手法。将所有对该变量的引用动作,替换为对它赋值的那个表达式自身

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
double basePrice = anOrder.basePrice();
return (basePrice > 10000 );

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
return (anOrder.basePrice > 1000);

4.以查询取代临时变量

你的程序以一个临时变量保存某一表达式的运算结果。将这个表达式提炼到一个独立的函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可被其他函数使用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
double basePrice = quantity * timePrice;
if(basePrice > 1000){
    return basePrice * 09.5;
} else {
    return basePrice * 0.98;
}

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(basePrice() > 1000){
    return basePrice * 09.5;
} else {
    return basePrice * 0.98;
}
double basePrice(){
    return quantity * timePrice;
}

临时变量只在所属的函数中可见,如果把临时变量替换为一个查询,那么同一个类中的所有函数都将可以获得这个份信息,这将带给你极大的帮助,使你能够为这个类编写更清晰的代码。

5.引入注释性变量

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if ((platform.toUpperCase().indexOf("MAC") > -1) && (browser.toUpperCase().indexOf("IE") > -1) && wasInitialized() && resize >0){
    //do smothing
}

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize >0;
if(isMacOs && isIEBrowser && wasInitialized() && wasResized){
    //do smothing
}

表达式有可能非常复杂难以理解。这种情况下,临时变量可以帮助你将表达式分解为比较容易管理的形式。

在条件逻辑中,你可以用这项重构将每个条件子句提炼出来,以一个良好命名的临时变量来解释对应条件子句的意义。另一种情况是:在较长的算法中,可以运用临时变量来解释每一步运算的意义。

6.分解临时变量

你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。针对每次赋值,创造一个独立、对应的临时变量

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
double temp = 2 * (height + width);
System.out.println(temp);
temp = height * width;
System.out.println(temp);

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
double perimeter = 2 * (height + width);
System.out.println(perimeter);
double area = height * width;
System.out.println(area);

如果临时变量被赋值超过一次,就意味着它们在函数中承担了一个以上的责任。如果临时变量承担多个责任,它就应该被替换为多个临时变量。每个变量只承担一个责任,同一个临时变量承担两件不同的事情会令代码阅读者糊涂

7.移除对参数的赋值

代码对一个参数进行复制。以一个临时变量取代该参数的位置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int discount (int inputVal, int quantity, int yearToData){
    if(inputVal > 50) inputVal -= 2;
}

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int discount (int inputVal, int quantity, int yearToData){
    int result = inputVal;
    if(inputVal > 50) result -= 2;
}

如果代码的语义是按引用传递的,请在调用段检查调用后是否还使用了这个参数。

8.替换算法

想要把某个算法替换为另一个更清晰的算法。将函数本体替换成为另一个算法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String foundPerson(String[] people){
    for(int i = 0;i < people.length; i++){
        if(people[i].equals("Don")){
            return "Don";
        }
        if(people[i].equals("John")){
            return "John";
        }
        if(people[i].equals("Kent")){
            return "Kent";
        }
    }
    return "";
}

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
String foundPerson(String[] people){
    List candidates = Arrays.asList(new String[]{"Don", "John", "Kent"});
    for(int i = 0;i < people.length; i++){
        if(candidates.contains(people[i])){
            return prople[i];
        }
    }
    return "";
}

四.在对象之间搬移特性

在对象设计过程中,决定把责任放在哪儿是即使不是最重要的事,也是最重要的事之一。常常只使用搬移函数搬移字段简单地移动对象行为,就可以解决这些问题。如果这两个重构手法都需要用到,我会首先使用搬移字段,再使用搬移方法。如果一个类承担了太多责任而变得臃肿不堪,这种情况下会使用提炼类将一部分责任分离出去。如果一个类变得太不负责任,使用将类内联化将它融入到另一个类中。

1.搬移函数

你的程序中,有个函数与其所驻类之外的另个一类进行跟过的交流:调用后者或被后者调用。在该函数最长引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或者将旧函数完全移除

如果一个类有太多行为,或如果一个类与另一个类有太多合作而高度耦合,就需要搬移函数。可以是系统中的类更简单

2.搬移字段

程序中,某个字段被其所驻类之外的另一个类更多的用到。在目标类新建一个字段,修改原字段的所有用户,令他们改用新字段

3、提炼类

某个类做了应该由两个类做的事。建立一个新类,将相关字段和函数从就类搬到新类

4.将类内联化

某个类没有做太多的事情,不在承担足够责任,不再有的那单独存在的理由。将这个类的所有特性搬移到另一个类中,然后移除原类

5.隐藏“委托关系”

客户通过一个委托类来调用另一个对象。在服务类上建立客户所需要的所有函数,用来隐藏委托关系

封装意味每个对象都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的对象就会比较少。

如果某个客户先通过服务对象的字段得到另一个对象,然后调用后者的函数。那么客户就必须知晓这一层委托关系。万一委托关系变化,客户也要相应变化。

6.移除中间人

某个类做了过多的简单委托。让客户直接调用委托类。每当客户要使用手委托类的新特性时,你就必须在服务端添加一个简单委托函数。随着受委托类的特性越来越多,这一过程会让你很痛苦。

7.引入外加函数

你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数形式传入一个服务类实例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 Date newStart = new Date(year, month, date + 1); 

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Date newStart = nextDay(nowDate);
private static Date nextDay(Date arg){
    retrun new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);
}

如果可以修改源码,你可以自行添加一个新函数;如果不能,你就得在客户端编码,补足你要的那个函数

8.引入本地扩展

你需要为服务类踢狗一些额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类

五.重新组织数据

1.自封装字段

直接访问一个字段。为这个字段建立取值/设值函数,并且只以这些函数来访问字段

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private int low, high;
boolean includes(int arg){
    retrun arg >= low && arg <= high;
}

替换为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private int low, high;
boolean includes(int arg){
    retrun arg >= getLow() && arg <= getHigh();
}
int getLow(){
    retrun low;
}
int getHigh(){
    return high;
}

在“字段访问方式”这个问题上,存在两种截然不同的观点:

  • 在该变量定义所在的类中,你可以自由的访问。
  • 即使在这个类中你也应该只使用访问函数间接访问。间接访问的好处是:子类可以通过复写一个函数而改变获取数据的途径;它支持更灵活的数据管理方式,例如延迟初始化。

2.以对象取代数据值

你有一个数据项,需要与其他数据和行为一起使用才有意义。将数据项变为对象。一开始你肯能会用一个字符串来表示“电话号码”概念,但是随后你会发现,电话号码需要“格式化”、“区号”之类的行为。这时候就需要为带替换的数值新建一个类。

3.将值对象改为引用对象

你从一个类衍生出许多彼此相等的实例,希望将它们替换为同一个对象。将这个值对象变成引用对象

4.将引用对象改为值对象

你有一个引用对象,很小且不可改变,而且不易管理。将它变成一个值对象

5.以对象取代数组

你有一个数组,其中的元素各自代表不同的东西。以对象替换数组。对于数组中的每个元素,以一个字段来表示

6.复制“被监视数据”

你有一些领域数据置身GUI控件中,而领域函数需要访问这些数据。将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据

7.将单向关联改为双向关联

两个类都需要使用对方特性,但其间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接

8.将双向关联改为单向关联

两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。去除不必要的关联

9.以字面常量取代魔数

你有一个字面数值,带有特别含义。创造一个常量,根据其意义为它命名,并将上述的字面数值替换为常量

10.封装字段

你的类中存在一个public字段。将它声明为private,并提供相应的访问函数

11.封装集合

有个函数返回一个集合。让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数

六.简化条件表达式

1.分解条件表达式

有一复杂的条件语句。从if、then、else三个段落中分别提炼出独立函数

2.合并表达式

你有一系列条件测试,都得到相同结果。将这些测试合并为一个条件表达式,并将这个条件表达式提炼成一个独立函数

3.合并重复的条件代码

在表达式的每个分支上都执行了相同的一段代码。将这段重复代码搬移到条件表达式之外

4.移除控制标记

在一系列布尔表达式中,某个变量带有”控制标记”的作用。以break/return语句取代控制标记

5.以多态取代条件表达式

有个条件表达式根据对象类型的不同而选择不同的行为。将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数

七.简化函数调用

1.函数改名

函数的名称未能揭示其用途。修改函数名称

2.添加参数

某个函数需要从调用端得到更多信息。为此函数添加一个对象参数,让该对象带仅函数所需信息

3.移除参数

函数本体不再需要某个参数。去除参数

4.分离查询函数和修改函数

某个函数既返回对象状态值,又修改对象值。建立两个不同函数,其中一个负责查询,另一个负责修改

5.令函数携带参数

若干函数做了类似的工作,但在函数本体中包含了不同的值。建立单一函数,以参数表达那些不同的值

有这样两个函数:它们做着类似的工作,但因少数几个值致使行为略有不同。在这种情况下,你可以将这些各自分离的函数同一起来,并通过参数来处理那些变化情况,用以简化问题。

6.以明确函数取代参数

你有一个函数,其中完全取决于参数值而采用不同行为。针对该参数的每一个可能值,建立一个独立函数

如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值做出不同的行为,那么就应该使用本项重构。

7.保持对象完整

从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象

8.以函数取代参数

对象调用某个函数,并将所得结果作为参数,传递给另一函数,而接受该参数的函数本身也能够调用前一个函数。让参数接受者去除该参数,直接调用前一个函数

9.引入参数对象

某些参数总是很自然的同时出现。以一个对象取代这些参数

10.移除设值函数

类中某个字段在对象创建时被设值,然后不再改变。去掉该字段的所有设值函数

11.隐藏函数

某个函数,从来没有被其他任何类用到 。将函数修改为private

12.以工厂函数取代构造函数

希望在创建对象时不仅仅是做简单的建构动作 。将构造函数替换为工厂函数

八.处理概括关系

1.字段上移

两个子类拥有相同的字段。将该字段移至超类

2.函数上移

有些函数在各子类中产生完全相同的结果。将该函数移至超类

3.构造函数本体上移

各个子类中有一些构造函数本体几乎完全一致 。在超类中新建一个构造函数,并在子类构造函数中调用它

4.函数下移

超类中的某个函数只与部分(而非全部)子类用到。将函数移到相关的子类中

5.字段下移

超类中的某个字段只被部分(而非全部)子类用到。将字段移到需要它的子类中

6.提炼子类

类中的某些特性只被某些(而非全部)实例用到。新建一个子类,将上述部分的特性移到子类中

7.提炼超类

两个类有相似特性。为这两个类建立一个超类,将相同特性移至超类

8.提炼接口

若干客户使用类接口中的同一子集,或两个类的接口有部分相同。将相同的子集提炼到一个独立接口中

9.折叠继承体系

超类和子类之间无太大区别。将它们合为一体

10.塑造模板函数

子类中某些函数以相同顺序执行类似操作,但各操作细节略有不同。将操作放进独立函数(保持签名相同),然后将它们移至超类

11.以委托取代继承

某个子类只使用超类接口中的一部分或根本不需要继承而来的数据。子类新建字段保存超类,调整子类函数为委托超类,取消继承关系

12.以继承取代委托

你在两个类中使用委托关系,并经常为整个接口编写许多极简单的委托函数。让委托类继承受托类

作者:小村医 原文:jianshu.com/p/3f04b6aebad2

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Kirito的技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
关于重构的总结
重构是对软件内部的一种调整,目的是在不改变软件可观察行为的前提下,提高可理解性,降低其修改成本。
陨石坠灭
2020/01/21
1K0
关于重构的总结
重构-改善既有代码的设计:重新组织函数的九种方法(四)
函数过长或者逻辑太混乱,重新组织和整理函数的代码,使之更合理进行封装。
黄规速
2022/04/14
4250
重构-改善既有代码的设计:重新组织函数的九种方法(四)
重构·改善既有代码的设计.03之重构手法(上)
之前的重构系列中,介绍了书中提到的重构基础,以及识别代码的坏味道。今天继续第三更,讲述那些重构手法(上)。看看哪些手法对你的项目能有所帮助…
有一只柴犬
2024/01/25
1670
重构·改善既有代码的设计.03之重构手法(上)
开发中滥用面向对象,你是否违背了编程原则
面向对象程序的一个最明显特征就是:少用 switch 和 case 语句。从本质上说,switch 语句的问题在于重复(if 序列也同样如此)。你常会发现 switch 语句散布于不同地点。如果要为它添加一个新的 case 子句,就必须找到所有 switch语句并修改它们。面向对象中的多态概念可为此带来优雅的解决办法。
李红
2019/05/29
8880
重构·改善既有代码的设计.04之重构手法(下)完结
本文是代码重构系列的最后一篇啦。前面三篇《重构·改善既有代码的设计.01之入门基础》、《重构·改善既有代码的设计.02之代码的“坏味道”》、《重构·改善既有代码的设计.03之重构手法(上)》介绍了基础入门,代码异味,还有部分重构手法。今天继续总结重构手法下篇,从条件表达式、函数调用、以及类继承关系上阐述了各种重构手法,希望对项目能有所帮助。另外本文更新后该系列就完结了,感谢各位看官的指点。
有一只柴犬
2024/01/25
2190
重构·改善既有代码的设计.04之重构手法(下)完结
【笔记】《重构: 改善既有代码的设计》
好久不见, 忙完一阵子开始继续更新了, 先让我水一篇杂的. 前段时间为了更好地重构自己的代码而看了《重构: 改善既有代码的设计》这本书, 以下是当时阅读期间做的简单笔记.
ZifengHuang
2021/11/30
1.6K0
【笔记】《重构: 改善既有代码的设计》
读《重构:改善既有代码的设计》
一个项目运行久了,经过业务需求的迭代,开发人员的变更,总会产生一些质量不高的代码,要么来源于对某些业务理解的不太深,要么来源于对一些紧急变更的后遗症,往往遇到这种情况,我们会适时的引入重构,避免破窗效应,让一个项目越来越杂乱。 重构其实不仅可以重新梳理下我们的业务场景,梳理我们代码的逻辑,让其更贴合业务,更重要的是可以让开发人员有机会再次设计我们的系统,结合一些更好的开源项目和技术,提升团队的技术氛围。 每一次重构其实对于一个项目来说都是无比艰难的决定,上有新业务的需求,下有重构的使命,时间紧
高广超
2018/12/12
6960
代码重构:函数重构的 7 个小技巧
Extract Method 的重构手法是将多个 println() 抽离到独立的函数中(函数需要在命名上,下点功夫),这里对抽离的函数命名有 2 个建议:
phoenix.xiao
2021/10/20
6860
重构-改善既有代码的设计:坏代码的味道,编写代码20多宗罪(三)
如果你在一个以上的地点看到相同的程序结构,那么可以肯定:设法将它们和而为一,程序会变得更好。最常见的“重复代码”就是一个类内的两个函数含有相同的表达式。另一种常见情况就是两个互为兄弟的子类内含有相同的表达式。   1)同一个类的2个函数含有相同的表达式,这时可以采用Extract Method(提炼函数)提炼出重复的代码,然后让这2个地点都调用被提炼出来的那段代码。   2)两个互为兄弟的子类内含相同表达式,只需对2个类都是用Extract Method(提炼函数),然后对被提炼出来的函数是用Pull Up Method (方法上移) ,将它推入超类。如果代码之间只是类似, 并非完全相同,那么就得运用Extract Method(提炼函数 将相似部分和差异部分隔开,构成单独一个的函数。然后你可能发现可以运用Form Template Method (塑造模板函数)获得一个 Template Method设计模式。如果有些函数以不同的算法做相同的事,你可以选择其中较清晰地一个,并是用 Substitute Algorithm (替换算法)将其他函数的算法替换掉。   如果2个毫不相关的类出现 重复代码,你应该考虑对其中一个运用 Extract Class (提炼类),将重复代码提炼到一个独立类中,然后在另一个类内使用这个新类。但是,重复代码所在的函数可能只应该属于某个类,另一个类只能调用它,抑或这个函数可能属于第三个类,而另2个类应该引用这第三个类。你必须决定这个函数放在哪儿最合适,并确保它被安置后就不会再在其他任何地方出现。
黄规速
2022/04/14
6690
重构-改善既有代码的设计:坏代码的味道,编写代码20多宗罪(三)
《重构-代码整洁之道TypeScript版》第2天
---- 昨天我们发了第一篇,今天让我们来继续第二天。先来回顾一下昨天我们都实现了哪些: Add Parameter(添加参数) Change Bidirectional Association to
疯狂的技术宅
2020/08/04
6940
《重构-代码整洁之道TypeScript版》第3天
---- 今天让我们来继续第3天,老规矩先来回顾一下昨天我们都实现了哪些: Change Reference to Value(将引用对象改为值对象) Change Value to Referenc
疯狂的技术宅
2020/08/04
6120
重构全面总结
《重构》为我们带来了一种改进代码的高效过程,从而彻底改变了面向对象设计的方式:
黄规速
2022/04/14
3630
如何解决代码腐败的味道
一. Duplicated Code(重复代码) 如果你在一个以上的地点看到相同的程序结构,设法将他们合而为一,程序会变得更好。 同一个类的两个函数含有相同的表达式,采用Extract Method(提炼函数)提炼出重复的代码。 两个互为兄弟的子类含有相同的表达式,首先对两个类都使用Extract Method(提炼函数),然后再对提炼出来的代码使用Pull Up Method(函数上移),将它推入超类。 如果代码之间只是类似,并非完全相同,运用Extract Method(提炼函数)将相似部分和差异部
微观技术
2020/08/20
8830
《重构-代码整洁之道TypeScript版》第一天
看到这个标题你的脑海中一定会浮现出两本书,一本就是,《重构--改善既有代码设计》 和 《代码整洁之道》 。这确实是两本非常伟大的图书,但是很遗憾里面提供的 code 都是 Java 的版本。《重构--改善既有代码设计》 的第2版提供了 JavaScript 的版本,已经非常方便我们前端同学阅读了,但是在 TypeScrip 如此火热的今天,缺了 TS 的版本,始终觉得是些遗憾,所以老袁打算每天拿出一些时间将一些非常经典的案例,结合老袁十年的经验总结到一块使用 TS 重写,希望能陪伴各位的技术成长之路,我会从大致如下方向跟各位共同分享:
疯狂的技术宅
2020/08/04
1.1K0
怎么让代码不再臃肿,写的像诗一样优雅
类似其他大部分坏味道,基本类型偏执诞生于类初建的时候。一开始,可能只是不多的字段,随着表示的特性越来越多,基本数据类型字段也越来越多。
李红
2019/05/29
8970
《重构-代码整洁之道TypeScript版》第4天
---- 今天让我们来继续第4天,老规矩先来回顾一下昨天我们都实现了哪些: Consolidate Conditional Expression(合并条件表达式) Consolidate Duplic
疯狂的技术宅
2020/08/04
4360
【重构】 代码的坏味道总结 Bad Smell (一) (重复代码 | 过长函数 | 过大的类 | 过长参数列 | 发散式变化 | 霰弹式修改)
膜拜下 Martin Fowler 大神 , 开始学习 圣经 重构-改善既有代码设计 .
韩曙亮
2023/03/27
8410
重构-改善既有代码的设计:处理概括关系 (九)
如果各子类是分别开发的,或者是在重构过程中组合起来的,你常会发现它们拥有重复特性,特别是字段更容易重复。这样的字段有时拥有相似的名字,但也并非绝对如此。判断若干字段是否重复,唯一的办法就是观察函数如何使用它们。如果它们被使用的方式很相似,你就可以将它们归纳到超类去。
黄规速
2022/04/14
5040
重构-改善既有代码的设计:处理概括关系 (九)
RefactoringGuru 代码异味和重构技巧总结
【代码膨胀】是代码、方法和类,它们的规模已经增加到了难以处理的地步。通常,这些异味不会立即出现,而是随着程序的演化而积累(尤其是当没有人努力根除它们的时候)。
ApacheCN_飞龙
2022/04/02
2K0
Java开发中存在这样的代码,反而影响整体整洁和可读性
许多编程技术都建立在库类的基础上。库类的作者没用未卜先知的能力,不能因此责怪他们。麻烦的是库往往构造的不够好,而且往往不可能让我们修改其中的类以满足我们的需要。
李红
2019/05/29
5140
推荐阅读
相关推荐
关于重构的总结
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档