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

2023-12-07 16:38

本文主要是介绍《重构:改善既有代码的设计》-学习笔记二(+实战解析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

 目录

Long Parameter List(过长参数列)
Divergent Change(发散式变化)
Shotgun Surgery(散弹式修改)
Feature Envy(依恋情结)
Data Clumps(数据泥团)
Primitive Obsession(基本型别偏执)
Switch Statements(switch惊悚现身)

 

 

Long Parameter List(过长参数列)

上一节有提过,当函数的入参过多时,可以用第三招,参数对象化,把参数封装成对象,然后参数对象当成函数的入参,达到减少参数的作用。
除了参数对象化,还可以使用另一种方法来处理。
这种方法叫做:Replace Parameter with Method(以函数取代参数)

优化思路

前提,这个参数是只被赋值一次的
1、如果有必要,将参数的计算过程提炼到一个独立函数中。
2、将函数内有使用参数的地方替换成独立函数。
3、每次替换后,测试。
4、全部替换完成后,最后把这个参数删除。
eg:未优化的代码
public double getPrice() {int basePrice = _quantity * _itemPrice;int discountLevel;if (_quantity > 100) discountLevel = 2;else discountLevel = 1;double finalPrice = discountedPrice (basePrice, discountLevel);return finalPrice;}private double discountedPrice (int basePrice, int discountLevel) {if (discountLevel == 2) return basePrice * 0.1;else return basePrice * 0.05;}

优化 1,2,3步骤 优化参数basePrice

public double getPrice() {int basePrice = getBasePrice();int discountLevel;if (_quantity > 100) discountLevel = 2;else discountLevel = 1;double finalPrice = discountedPrice ( discountLevel);return finalPrice;}private double discountedPrice ( int discountLevel) {if (discountLevel == 2) return getBasePrice() * 0.1;else return getBasePrice() * 0.05;}private int getBasePrice(){return _quantity * _itemPrice;}

优化4步骤 去掉参数basePrice

public double getPrice() {int discountLevel;if (_quantity > 100) discountLevel = 2;else discountLevel = 1;double finalPrice = discountedPrice ( discountLevel);return finalPrice;}private double discountedPrice ( int discountLevel) {if (discountLevel == 2) return getBasePrice() * 0.1;else return getBasePrice() * 0.05;}private int getBasePrice(){return _quantity * _itemPrice;}

优化1,2,3,4步骤,去掉discountLevel参数,独立函数返回值要为discountLevel 最后赋值的值

public double getPrice() {double finalPrice = discountedPrice ();return finalPrice;}private double discountedPrice () {if (getDiscountLevel() == 2) return getBasePrice() * 0.1;else return getBasePrice() * 0.05;}private double getDiscountLevel(){if (_quantity > 100) return 2;else return 1;}  private double getBasePrice(){return _quantity * _itemPrice;}

从上述代码可看出,getPrice主函数finalPrice参数已经可以直接优化了。

public double getPrice() {return discountedPrice ();}
可以发现getPrice函数直接调用discountedPrice 函数,所以可用Inline Method(将函数内联化)
合并这两个函数
public double getPrice() {if (getDiscountLevel() == 2) return getBasePrice() * 0.1;else return getBasePrice() * 0.05;}private double getDiscountLevel(){if (_quantity > 100) return 2;else return 1;}  private double getBasePrice(){return _quantity * _itemPrice;}
我们只关心主函数的计算过程,一些过程性的计算,像上述这样,独立函数出来。代码逻辑会十分清晰,可读性很好。
存在一个重要的例外。如果明显不希望封装的对象与主对象之间存在某种依赖关系,可以把参数数据从封装对象中抽出来,当成函数的参数。也是合理的。
但是要注意,当参数列太多或者参数变化频繁时,就要考虑优化了。

Divergent Change(发散式变化)

你发现你想要修改的一个函数,却必须同时修改诸多不相关的函数,例如,当你想要添加一个新的产品类型,你需要同步修改对产品查找,显示,排序的函数。
有以上这些情况的话,就需要优化代码了
针对某一外界 变化的所有相应修改,都只应该发生在单一class中,而这个新class内的所有内容都应该反应该外界变化。
通过提炼类的方式,找出因着某特定原因而造成的所有变化,独立类。
问题原因:
通常,这种发散式修改是由于编程结构不合理或者“复制-粘贴式编程”。

优化思路

运用提炼类拆分类的行为。
如果不同的类有相同的行为,你可以考虑通过继承来合并类和提炼子类。
效果:
提高代码组织结构
减少重复代码

Shotgun Surgery(散弹式修改)

-- 注意霰弹式修改 与 发散式变化 区别 : 发散式变化是在一个类受多种变化影响, 每种变化修改的方法不同, 霰弹式修改是 一种变化引发修改多个类中的代码;

优化思路

1、代码集中到某个类中 : 使用 Move Method(搬移函数) 和 Move Field(搬移字段) 把所有需要修改的代码放进同一个类中;
2、 代码集中到新创建类中 : 没有合适类存放代码, 创建一个类, 使用 Inline Class(内联化类) 方法将一系列的行为放在同一个类中;
3、造成分散式变化 : 上面的两种操作会造成 Divergent Change(分散式变化), 使用Extract Class 处理分散式变化;

Feature Envy(依恋情结)

函数对某个class的兴趣高过对自己所处之 class的兴趣。无数次经验里,我们看到某个函数 为了计算某值,从另一个对象那儿调用几乎半打的取值函数。
影响:数据和行为不在一处,修改不可控。
解决方案:让数据和行为在一起,通过 Extract Method(提炼函数)和Move Method(搬移函数)的方法来处理,这函数到该去的地方。
例子:参考一个优秀博主提供的例子
https://blog.csdn.net/wxr0323/article/details/7884168

优化思路

1、函数全部数据来自另外一个类
      做法:将数据提炼到一个独立函数中  Move method。
2、函数部分数据来自另外一个类
      做法:将“部分数据”提炼到一个函数中 Move method。
3、函数的数据来自不同类
      做法:将数据分类,分别提炼各自的独立的函数,在将这些函数移到各自属于的类中。

Data Clumps(数据泥团)

数据泥团指的是经常一起出现的数据,比如每个方法的参数几乎相同,处理方式与过长参数列的处理方式相同,用Introduce Parameter Object(引入参数对象)将参数封装成对象。

优化思路

1、观察经常一起出现的数据;
2、通过提炼类的方法,放到属于他们的对象中;
3、用对象来代替这些数据;
4、编译测试。
eg:未优化代码
例子参考一个优秀博主提供的例子
https://blog.csdn.net/geniusxi/article/details/78581542
public class Car{// 奔驰public void printBenz(String brand, String model, Integer price, double power) {printBasicInfo(brand, model, price, power);getTax(power, price);}// 宝马public void printBmw(String brand, String model, Integer price, double power) {printBasicInfo(brand, model, price, power);getTax(power, price);}// 提炼打印基本信息方法private void printBasicInfo(String brand, String model, Integer price, double power) {System.out.println("品牌" + brand);System.out.println("型号:" + model);System.out.println("动力:" + power);System.out.println("价格:" + price);}// 提炼计算税费的方法private double getTax(double power, Integer price){double salePrice = price;if (price > 200000) {salePrice = price * 0.98;}if (power <= 1.6) {return salePrice * 0.05;} else {return salePrice * 0.1;}}
}

 

优化1,2步骤

 

上面代码方法中,我们发现方法的参数大致相同,这时候我们可以用参数对象化来处理。
public class CarEntity {private String brand;private String model;private Integer price;private Double power;public String getBrand() {return brand;}public void setBrand(String brand) {this.brand = brand;}public String getModel() {return model;}public void setModel(String model) {this.model = model;}public Integer getPrice() {return price;}public void setPrice(Integer price) {this.price = price;}public Double getPower() {return power;}public void setPower(Double power) {this.power = power;}}

优化3,4步骤

// 奔驰public void printBenz(CarEntity carEntity) {printBasicInfo(carEntity);// 计算税费getTax(carEntity);}// 宝马public void printBmw(CarEntity carEntity) {printBasicInfo(carEntity);getTax(carEntity);}private void printBasicInfo(CarEntity carEntity) {System.out.println("品牌" + carEntity.getBrand());System.out.println("型号:" + carEntity.getModel());System.out.println("动力:" + carEntity.getPower());System.out.println("价格:" + carEntity.getPrice());}// 计算税费private double getTax(CarEntity carEntity) {// 打折后价格double salePrice = carEntity.getPrice();if (carEntity.getPrice() > 200000) {salePrice = carEntity.getPrice() * 0.98;}if (carEntity.getPower() <= 1.6) {return salePrice * 0.05;} else {return salePrice * 0.1;}}
经过以上的优化,代码就更加健壮了。

Primitive Obsession(基本型别偏执)

写代码时总喜欢用基本类型来当参数,而不喜欢用对象。当要修改需求和扩展功能时,复杂度就增加了。

优化思路

1、如果你有一组应该总是被放在一起的属性或参数,可以用提炼类的方式来处理;
2、如果你在参数列中看到多个基本型数据,可以引用参数对象;
3、如果你发现自己正从array中挑选数据,可以用对象取代数组。
优化步骤1和2之前的例子说明了很多次,不再重复。
优化步骤3
用对象取代数组:你有一个数组(array),其中的元素各自代表不同的东西。就可以用对象来表示数组。
eg:
String[] row = new String[3];row [0] = "Liverpool";row [1] = "15";//对象取代数组Performance row = new Performance();row.setName("Liverpool");row.setWins("15");
Performance 对象里包含name属性和wins属性,且这两个属性被定义为private ,同时拥有get方法和set方法。

Switch Statements(switch惊悚现身)

面向对象程序的一个最明显特征就是:少用switch (或case)语句。从本质上说, switch语句的问题在于重复(duplication)。

优化思路

这种情况我们可以引用工厂 + 策略模式。用工厂把重复的switch提炼到一起构建成一个工厂类,策略模式把switch分支中执行的动作提炼成单独的类。
例子参考一个优秀博主提供的例子
https://blog.csdn.net/geniusxi/article/details/78581542
更多精彩内容,请等待后续更新。
文章也同步到了博客园:https://www.cnblogs.com/zenghw/p/9114428.html

 

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

作者:小虚竹
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

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

 

 

 

 

 

这篇关于《重构:改善既有代码的设计》-学习笔记二(+实战解析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/466585

相关文章

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型