我不是个伟大的程序员;我只是个有着一些优秀习惯的好程序员而己
本人比较直接,不说虚的,直接上干货。
目录
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(搬移函数)的方法来处理,这函数到该去的地方。 例子
优化思路
1、函数全部数据来自另外一个类 做法:将数据提炼到一个独立函数中 Move method。 2、函数部分数据来自另外一个类 做法:将“部分数据”提炼到一个函数中 Move method。 3、函数的数据来自不同类 做法:将数据分类,分别提炼各自的独立的函数,在将这些函数移到各自属于的类中。
Data Clumps(数据泥团)
数据泥团指的是经常一起出现的数据,比如每个方法的参数几乎相同,处理方式与过长参数列的处理方式相同,用
Introduce Parameter Object(引入参数对象)将参数封装成对象。
优化思路
1、观察经常一起出现的数据; 2、通过提炼类的方法,放到属于他们的对象中; 3、用对象来代替这些数据; 4、编译测试。 eg:未优化代码 例子参考一个优秀博主提供的例子
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分支中执行的动作提炼成单独的类。 例子参考一个优秀博主提供的例子