【前端】重构,有品位的代码05──搬移特性

写在前面

本文是《重构,有品位的代码》系列第五篇文章,前面文章主要介绍的重构手法是关于如何新建、移除或重命名程序的元素。当然,不只是只有这些手法,还有类型的重构也是很重要的,主要是在不同上下文间搬移元素。可以通过搬移函数手法在类与其他模块之间搬移函数,同样的也有搬移字段手法,还有其它手法将在本文中将逐一介绍...

站在用户的角度思考问题,与客户深入沟通,找到锡林浩特网站设计与锡林浩特网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站建设、做网站、企业官网、英文网站、手机端网站、网站推广、域名申请雅安服务器托管、企业邮箱。业务覆盖锡林浩特地区。

前情回顾:

  • 《重构,有品位的代码 01──走上重构之道》
  • 《重构,有品位的代码 02──构建测试体系》
  • 《重构,有品位的代码 03──常用重构手法》
  • 《重构,有品位的代码 04──封装》

常见的搬移特性手法

在平时开发中,经常会在代码中使用到搬移特性,但是并不知道是做了什么搬移特性,现在我们将常用的搬移特性手法进行总结如下:

  • 搬移函数
  • 搬移字段
  • 搬移语句到函数
  • 搬移语句到使用者
  • 以函数调用取代内联代码
  • 移动语句
  • 拆分循环
  • 以管道取代循环
  • 移除死代码

1. 搬移函数

模块化能够确保我们的代码块间的联系易于查找、直观易懂,能够保证相互关联的软件要素集中在一块,便于我们理解和管理。与此同时,随着对代码的理解加深,了解到那些软件要素如何组织最为恰当,此时需要通过不断地搬移元素进行重新模块化。

函数是存活在上下文中的,这个上下文可能是全局的,也有可能是当前所在模块进行提供的。而类即为主要的模块化手段,作为函数的上下文,此外通过函数嵌套的方式,外层函数也可为内层函数提供上下文。简而言之,模块可以为函数提供存活的上下文环境。

由于在某些代码频繁引用其他上下文中的元素,即与其他上下文的元素关系紧密,而对于自身上下文中的元素关心甚少,此时就可以考虑将联系密切的元素进行归纳,取得更好的封装效果。那么有以下情况,你可以进行搬移函数的操作:

  • 某段代码需要频繁调用别处函数
  • 在函数内部定义帮助函数在别处也有调用
  • 在类中定义函数

通常的,首先检查函数在当前上下文中引用的所有程序元素(包括变量和函数),考虑是否需要将它们进行搬移,并对待搬移函数是否具有多态性进行检查。将函数复制一份到目标上下文中,调整函数使得适应新的上下文。执行静态检查,设法从源上下文中正确引用目标函数,修改源函数,使之成为一个纯委托函数。

原始代码:

 
 
 
  1. class Account{
  2.   constructor(){
  3.     ....
  4.   }
  5.   get bankCharge(){
  6.     let result = 4.5;
  7.     if(this._daysOverdrawn> 0) result += this.overdraftCharge;
  8.   }
  9.   
  10.   get overdraftCharge(){
  11.     if(this.type.isPremium){
  12.       const basecharge = 10;
  13.       if(this.dayOverdrawn <= 7){
  14.         return baseCharge;
  15.       }else{
  16.         return baseCharge + (this.daysOverdrawn - 7) * 0.85;
  17.       }
  18.     }else{
  19.       return this.daysOverdrawn * 1.75;
  20.     }
  21.   }
  22. }

重构代码:

 
 
 
  1. class Account{
  2.   constructor(){
  3.     ...
  4.   }
  5.   get bankcharge(){
  6.     let result = 4.5;
  7.     if(this._daysOverdrawn> 0) result += this.overdraftCharge;
  8.   }
  9.   get overdraftCharge(){
  10.     return this.Type.overdraftCharge(this);
  11.   }
  12. }
  13. class AccountType{
  14.   constructor(){
  15.     ...
  16.   }
  17.   overdraftCharge(account){
  18.     if(this.isPremium){
  19.       const basecharge = 10;
  20.       if(account.dayOverdrawn <= 7){
  21.         return baseCharge;
  22.       }else{
  23.         return baseCharge + (account.daysOverdrawn - 7) * 0.85;
  24.       }
  25.     }else{
  26.       return account.daysOverdrawn * 1.75;
  27.     }
  28.   }
  29. }

2. 搬移字段

在开发中你是否会遇到一些糟糕的代码,使用了糟糕的数据结构,代码的逻辑并不清晰条理,更多的是各种纠缠不清,代码很多令人费解的无用代码。因此,通常可以做些预先的设计,设法获取最恰当的数据结构,而具备驱动设计方面的经验和知识,将有助于你设计数据结构。

当然,即使经验丰富、技能熟练,也会在设计数据结构的时候犯错,但是随着对问题理解的深入,对业务逻辑的熟悉,便会考虑到更深更全面。在过程中发现数据结构不适应需求,便要及时进行修缮,如果容许瑕疵存在便会导致代码复杂化,问题累积。

在你每次进行调用函数时,在传入一个参数时,总是需要伴随另外的字段作为参数传入,即修改一条记录同时需要修改另一条记录,那么意味着此处的字段位置放置错误。另外的,假设你更新某个字段,同时需要在多个结构中做出改变,那么就意味着你需要将此字段进行正确的搬移。

具体的,确保源字段已经进行良好封装,在目标对象上创建字段(及对应的访问函数)并执行静态检查,确保源对象里能够正常引用目标对象,即调整源对象的访问函数能够使用目标对象的字段。最后,移除源对象上的字段。

原始代码:

 
 
 
  1. class User{
  2.   constructor(name,age,getName){
  3.     this._getName = getName;
  4.     this._age = age;
  5.     this._name = name;
  6.   }
  7.   get getName(){
  8.     return this._getName;
  9.   }
  10. }
  11. class UserType{
  12.   constructor(firstName){
  13.     this._firstName = firstName;
  14.   }
  15. }

重构代码:

 
 
 
  1. class User{
  2.   constructor(age,name){
  3.     this._age = age;
  4.     this._name = name;
  5.   }
  6.   get getName(){
  7.     return this._name.getName;
  8.   }
  9. }
  10. class UserType{
  11.   constructor(firstName,getName){
  12.     this._firstName = firstName;
  13.     this._getName = getName;
  14.   }
  15.   get getName(){
  16.     return this._getName;
  17.   }
  18. }

3. 搬移语句到函数

在重构代码时有几条黄金准则,其中最重要的就是要“消除重复”代码,对重复语句进行抽象到函数中,通过调用函数来实现复杂代码的运行。

4. 搬移语句到调用者

作为搬砖码农的指责就是设计结构一致、抽象合宜的程序,而函数就是抽象的制胜法宝。当然所有的手段都并非放之四海而皆准的法则,随着系统能力的演变,最初设计的抽象边界逐渐向外扩散变得模糊,从原先单独整体、聚焦唯一点,分化成多个不同关注点。

而函数边界发生偏移,意味着之前多个地方调用的行为,现在需要会在不同点表现出不同的行为。这样,我们可以把不同表现行为从函数中挪出,将其搬移到调用处。

 
 
 
  1. printHtml(outData,onData.html);
  2. function printHtml(outData,html){
  3.   outData.write(`

    title:${html.title}

    `);
  4.   outData.write(`

    content:${html.content}

    `);
  5. }

即:

 
 
 
  1. printHtml(outData,onData.html);
  2. outData.write(`

    content:${onData.html.content}

    `);
  3. function printHtml(outData,html){
  4.   outData.write(`

    title:${html.title}

    `);
  5.   
  6. }

5. 以函数调用取代内联代码

使用函数可以将相关行为进行打包,提升代码的表达能力,清晰的解释代码的用途和作用,有助于消除重复的代码。如果某段内联代码是对已有函数进行重复,那么可以使用一个函数调用来取代内联代码,可以实现业务逻辑的抽象。

 
 
 
  1. let flag = false;
  2. for(const color of colors){
  3.   if(color === "yellow") flag = true;
  4. }

即:

 
 
 
  1. let flag = colors.includes("yellow");

6. 移动语句

如果有几行代码使用了相同的数据结构,那么可以使其关联使用,使得代码更易理解,而不是夹在其他数据结构中间。那么在我们写完代码后,需要进行审读,将关联性强的代码移动语句进行聚集。通常,移动语句作为其他重构代码的先提重构手段。

 
 
 
  1. const pricingPlan = rePricingPlan();
  2. const order = reOrder();
  3. let charge;
  4. const chargePerUnit = ricingPlan.uint;

重构代码:

 
 
 
  1. const pricingPlan = rePricingPlan();
  2. const chargePerUnit = ricingPlan.uint;
  3. const order = reOrder();
  4. let charge;

7. 拆分循环

在常规的开发中,会在一次循环中做多件事情,意图让其避免过高的时间复杂度。有的时候,在一次循环中代码过多、逻辑混乱,反而不便于我们日常理解。因此可以根据情况合理拆分循环,使其每次循环只做一件事情,更便于阅读使用。

 
 
 
  1. let averagePrice = 0;
  2. let totalCount = 0;
  3. for(const p in goods){
  4.   averagePrice += p.price;
  5.   totalCount += p.count;
  6. }
  7. averagePrice = averagePrice / totalCount;

重构代码:

 
 
 
  1. let averagePrice = 0;
  2. for(const p in goods){
  3.   averagePrice += p.price;
  4. }
  5. let totalCount = 0;
  6. for(const p in goods){
  7.   totalCount += p.count;
  8. }
  9. averagePrice = averagePrice / totalCount;

是不是看起来有点傻,当你在复杂代码中阅读会发现很清晰。

8. 以管道取代循环

在过去进行数组、对象遍历时,通常做法是使用循环进行迭代,当然也可以使用更好的语言结构———”集合管道“来处理迭代(map和filter等)。集合管道允许使用一组运算来描述集合迭代过程,其中每种运算都是一个集合。

通常做法:创建一个用于存放参与循环过程的集合的新变量,从c循环顶部开始,将循环内的每块行为依次搬移。在创建的集合变量中用管道运算进行替换,直到循环内的全部行为进行搬移完毕,最后将循环进行删除。

 
 
 
  1. const users = [];
  2. for(const item in arrs){
  3.   if(item.age === 20) users.push(item.name);
  4. }
  5. //重构代码
  6. const users = arrs
  7. .filter(item=>item.age === 20)
  8. .map(item=>item.name);

9. 移除死代码

在将项目部署在生产环境中,可能会因为代码量太大而造成更大的内存开销,无用代码会拖累系统的运行速度,导致项目进程缓慢。当然,多数的现在编译器会自动将无用代码进行移除,但是在你阅读理解代码逻辑和原理时,会让你花费时间去思索,耗费精力。在代码不再使用时,应当立即删除,当你突然又想使用时可以通过版本控制回滚。

 
 
 
  1. if(false){
  2.   ...
  3. }

这是一句无用代码,应当立刻删除。

小结

在本文中,主要介绍了搬移字段、搬移函数等搬移手段,也有单独对语句搬移、调整顺序的,也可以调整代码的位置,对循环进行拆分、使用管道替换等方法。

当前题目:【前端】重构,有品位的代码05──搬移特性
文章地址:http://www.mswzjz.cn/qtweb/news39/5789.html

攀枝花网站建设、攀枝花网站运维推广公司-贝锐智能,是专注品牌与效果的网络营销公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 贝锐智能