《重构:改善既有代码的设计》-学习笔记一(+实战解析)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 《重构:改善既有代码的设计》-学习笔记一(+实战解析)

我不是个伟大的程序员;我只是个有着一些优秀习惯的好程序员而己


本人比较直接,不说虚的,直接上干货。


目录


   Duplicated Code(重复的代码)


   Long Method(过长函数)


    Long Parameter List(过长参数列)


   Large Class(过大类)


提前总结就是四招:


   一、重复的代码提炼成函数


   二、把过长的函数变小


   三、参数列太长或变化太频繁,参数对象化


   四、大招:类的代码行数太多,要考虑提炼子类。



第一招 重复的代码提炼成函数

   第一种情况是:同一个class内的两个函数含有相同表达式(expression)。image.pngimage.pngimage.pngimage.pngimage.png优化的思路1

1、在各个subclass 中分解目标函数,把有差异的部分变成入参,封装成一个模板函数。


2、把模板函数放到父类中。


3、子类根据需要输入不同的入参,得到需要的结果。


*********************************************************************************


如果是相似的表达式,差异的地方不好抽共性,则用模板函数设计模式来处理。


这里用到了JAVA的两个特性,继承和覆写(overrides)。


优化的思路2

1、在各个subclass 中分解目标函数,使分解后的各个函数要不完全相同,要不完全不同。


2、父类有一个主函数包含完全相同的函数和完全不同的函数:相同的函数,抽到父类中,不相同的函数在父类中定义一个函数。


3、子类继承父类,然后覆写完全不同的函数,再调用主函数可得到期望的结果。


第二招 把过长的函数变小

百分之九十九的场合里,要把函数变小,只需使用Extract Method(第一招)。找到函数中适合集在一起的部分,将它们提炼出来形成一个新函数。


如果函数内有大量的参数和临时变量,它们会对你的函数提炼形成阻碍。这时就要用Replace Temp with Query来消除这些临时变量


Replace Temp with Query(以查询取代临时变量)


   优化思路

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


2、将该临时变量声明为final


3、编译:这可确保该临时变量的确只被赋值一次。


4、将临时变量等号右侧部分提炼到一个独立函数中;


5、首先将函数声明为private。日后你可能会发现有更多class需要使用 它,彼时你可再放松对它的保护。


6、编译,测试:确保提炼出来的函数无任何连带影响(副作用),结果不变;


7、把临时变量全替换成独立出来的函数;


以上,over!


例子:未优化代码

image.pngimage.pngimage.png通过以上的优化,一个大函数,已经变成了多个小函数,重点是代码的可读性提高了,顺带的代码量变少。


第三招 参数对象化

当你看到一个函数的入参有四,五个,甚至更多时,且好几个函数都使用这组入参,这时就要用参数对象化来优化代码。这些函数可能隶属同一个class,也可能隶属不同的classes 。这样一组参数就是所谓的Date Clump (数据泥团)」。这时用一个对象封装这些参数,再用对象取代它们。


优化思路

1、入参有四,五个,甚至更多时,就要着手优化;


2、用一个新的class封装入参,并把这些参数设置为private严格保护起来,写这些参数的get方法和set方法。


3、原函数的入参变成这个新的class对象,函数里的参数用class对象对应的属性替换。


4、编译测试;


5、将原先的参数全部去除之后,观察有无适当函数可以运用Move Method 搬移到参数对象之中。


例子:未优化的代码

@Autowired
  private AddressService addressService;
  public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,String addressName,String mobile,String zipCode,String consignee){
    return addressService.inquireAddressList(pageNum,pageSize,addressName,mobile,zipCode,consignee);
  }

优化

@Autowired
  private AddressService addressService;
  public List inquireAddressListAccount( Integer pageNum ,Integer pageSize,InquireAddressListInput output){
    return addressService.inquireAddressList(pageNum,pageSize,output);
  }
  public class InquireAddressListInput(){
    private String addressName;
    private String mobile;
    private String zipCode;
    private String consignee;
  public String getConsignee() {
    return consignee;
  }
  public void setConsignee(String consignee) {
    this.consignee = consignee;
  }
  public String getMobile() {
    return mobile;
  }
  public void setMobile(String mobile) {
    this.mobile = mobile;
  }
  public String getZipCode() {
    return zipCode;
  }
  public void setZipCode(String zipCode) {
    this.zipCode = zipCode;
  }
  public String getAddressName() {
    return addressName;
  }
  public void setAddressName(String addressName) {
    this.addressName = addressName;
  }
  }

第四招 大招-提炼类和提炼子类

如果想利用单一class做太多事情,其内往往就会出现太多instance变量。一旦如此,Duplicated Code也就接踵而至了。


Extract Class 是Extract Subclass 之外的另一种选择,两者之间的抉择其实就是委托(delegation)和继承(inheritance)之间的抉择。


情况一:某个class做了应该由两个classes做的事。(Extract Class)


优化思路1

1、明确每个class所负的责任,该做什么事情;


2、建立一个新class,用以表现从旧class中分离出来的责任;


3、建立「从旧class访问新class」的连接关系;


4、每次搬移后,编译、测试。


5、决定是否让新的class曝光。


例子:未优化的代码image.pngimage.pngimage.png情况二:class 中的某些特性(features)只被某些(而非全部)实体(instances)用到。Extract Subclass(提炼子类)



优化思路2

1、为source class 定义一个新的subclass


2、为这个新的subclass 提供构造函数。


   简单的作法是:让subclass 构造函数接受与superclass 构造函数相同的参数,并通过super 调用superclass 构造函数;


3、找出调用superclass 构造函数的所有地点。如果它们需要的是新建的subclass , 令它们改而调用新构造函数。


   如果subclass 构造函数需要的参数和superclass 构造函数的参数不同,可以使用Rename Method 修改其参数列。如果subclass 构造函数不需要superclass 构造函数的某些参数,可以使用Rename Method 将它们去除。


   如果不再需要直接实体化(具现化,instantiated)superclass ,就将它声明为抽象类。


4、逐一使用Push Down Method 和 Push Down Field 将source class 的特性移到subclass 去。


5、每次下移之后,编译并测试。


例子:未优化代码


--用来决定当地修车厂的工作报价:

class JobItem ...
   public JobItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {
       _unitPrice = unitPrice;
       _quantity = quantity;
       _isLabor = isLabor;
       _employee = employee;
   }
   public int getTotalPrice() {
       return getUnitPrice() * _quantity;
   }
   public int getUnitPrice(){
       return (_isLabor) ?
            _employee.getRate():
            _unitPrice;
   }
   public int getQuantity(){
       return _quantity;
   }
   public Employee getEmployee() {
       return _employee;
   }
   private int _unitPrice;
   private int _quantity;
   private Employee _employee;
   private boolean _isLabor;
 class Employee...
   public Employee (int rate) {
       _rate = rate;
   }
   public int getRate() {
       return _rate;
   }
   private int _rate;

image.pngimage.png

 class LaborItem extends JobItem {
  public LaborItem (int unitPrice, int quantity, boolean isLabor, Employee employee) {
       super (unitPrice, quantity, isLabor, employee);
   }
    public LaborItem (int quantity, Employee employee) {
       super (0, quantity, true, employee);
   }
   public Employee getEmployee() {
       return _employee;
   }
 }
//因为_employee 值域也将在稍后被下移到LaborItem ,所以我现在先将它声明为protected。
class JobItem...
  protected Employee _employee;

image.pngimage.pngimage.png使用某项值域的函数全被下移至subclass 后,我就可以使用 Push Down Field 将值域也下移。

最后的结果就是:

public class JobItem {
    protected  JobItem (int unitPrice, int quantity) {
         _unitPrice = unitPrice;
         _quantity = quantity;
     }
     public int getTotalPrice() {
         return getUnitPrice() * _quantity;
     }
     public int getUnitPrice(){
         return _unitPrice;
     }
     public int getQuantity(){
         return _quantity;
     }
     private int _unitPrice;
     private int _quantity;
}
//
public class LaborItem extends JobItem {
  private Employee _employee;
  public LaborItem(int quantity, Employee employee) {
    super(0, quantity);
    _employee = employee;
  }
  public Employee getEmployee() {
    return _employee;
  }
  public int getUnitPrice() {
    return _employee.getRate();
  }
}
//public class Employee {
  public Employee(int rate) {
    _rate = rate;
  }
  public int getRate() {
    return _rate;
  }
  private int _rate;
}

一个class如果拥有太多代码,也适合使用Extract Class和Extract Subclass。

想重构代码,直接把以上四招看情况用上,更多精彩内容,请等待后续更新。



***************************************************************************

作者:小虚竹

欢迎任何形式的转载,但请务必注明出处。

限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。



我不是个伟大的程序员,我只是个有着一些优秀习惯的好程序员而己


目录
打赏
0
0
0
0
28
分享
相关文章
昇腾 msmodelslim w8a8量化代码解析
msmodelslim w8a8量化算法原理和代码解析
117 5
HarmonyOS Next~鸿蒙应用框架开发实战:Ability Kit与Accessibility Kit深度解析
本书深入解析HarmonyOS应用框架开发,聚焦Ability Kit与Accessibility Kit两大核心组件。Ability Kit通过FA/PA双引擎架构实现跨设备协同,支持分布式能力开发;Accessibility Kit提供无障碍服务构建方案,优化用户体验。内容涵盖设计理念、实践案例、调试优化及未来演进方向,助力开发者打造高效、包容的分布式应用,体现HarmonyOS生态价值。
99 27
JSON数据解析实战:从嵌套结构到结构化表格
在信息爆炸的时代,从杂乱数据中提取精准知识图谱是数据侦探的挑战。本文以Google Scholar为例,解析嵌套JSON数据,提取文献信息并转换为结构化表格,通过Graphviz制作技术关系图谱,揭示文献间的隐秘联系。代码涵盖代理IP、请求头设置、JSON解析及可视化,提供完整实战案例。
188 4
JSON数据解析实战:从嵌套结构到结构化表格
可穿戴设备如何重塑医疗健康:技术解析与应用实战
可穿戴设备如何重塑医疗健康:技术解析与应用实战
79 4
Java机器学习实战:基于DJL框架的手写数字识别全解析
在人工智能蓬勃发展的今天,Python凭借丰富的生态库(如TensorFlow、PyTorch)成为AI开发的首选语言。但Java作为企业级应用的基石,其在生产环境部署、性能优化和工程化方面的优势不容忽视。DJL(Deep Java Library)的出现完美填补了Java在深度学习领域的空白,它提供了一套统一的API,允许开发者无缝对接主流深度学习框架,将AI模型高效部署到Java生态中。本文将通过手写数字识别的完整流程,深入解析DJL框架的核心机制与应用实践。
96 3
|
2月前
|
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
91 5
【实战解析】smallredbook.item_get_video API:小红书视频数据获取与电商应用指南
本文介绍小红书官方API——`smallredbook.item_get_video`的功能与使用方法。该接口可获取笔记视频详情,包括无水印直链、封面图、时长、文本描述、标签及互动数据等,并支持电商场景分析。调用需提供`key`、`secret`和`num_iid`参数,返回字段涵盖视频链接、标题、标签及用户信息等。同时,文章提供了电商实战技巧,如竞品监控与个性化推荐,并列出合规注意事项及替代方案对比。最后解答了常见问题,如笔记ID获取与视频链接时效性等。
GraphQL开发工具选型指南:Apipost高效调试与文档生成实战解析
本文深入解析了GraphQL开发工具Apipost在高效调试与文档生成方面的优势,对比同类工具Apifox,突出其可视化界面、实时调试及自动化文档生成等特性。Apipost通过智能代码补全、错误提示等功能简化复杂Query编写,支持一键生成标准化文档,显著提升开发效率和团队协作效果,尤其适合中大型团队应对复杂业务场景。
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
183 29

推荐镜像

更多
下一篇
oss创建bucket
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等