您现在的位置是:首页 >技术杂谈 >Java代码重构学习笔记-简化条件表达式网站首页技术杂谈
Java代码重构学习笔记-简化条件表达式
Decompose Conditional (分解条件表达式)
它的主要目的是将复杂的条件语句分解为多个简单的条件语句,从而提高代码的可读性和可维护性。
举个例子,假设有一个计费系统,其中包含一个 calculateFee 方法,负责根据用户的账单信息计算出用户的费用。最初的实现中,calculateFee 方法使用了一个复杂的 if-else 语句来判断不同的费用类型,并根据不同的条件进行计算,如下所示:
public double calculateFee(Bill bill) {
double fee = 0;
if (bill.getPlan() == PlanType.A) {
if (bill.getUsage() > 100) {
fee = 100 * bill.getBaseRate() + (bill.getUsage() - 100) * bill.getOverRate();
} else {
fee = bill.getUsage() * bill.getBaseRate();
}
} else if (bill.getPlan() == PlanType.B) {
if (bill.getUsage() > 200) {
fee = 200 * bill.getBaseRate() + (bill.getUsage() - 200) * bill.getOverRate();
} else {
fee = bill.getUsage() * bill.getBaseRate();
}
if (bill.getAccounts() > 1) {
fee -= fee * 0.05;
}
}
return fee;
}
这段代码使用了嵌套的 if-else 语句来判断不同的费用类型和不同的条件,并根据不同的条件进行计算。这样的代码很难阅读和理解,并且容易出现 bug。
为了解决这些问题,可以使用 “Decompose Conditional” 重构方法。具体实现方式是将嵌套的 if-else 语句分解为多个独立的语句,每个语句负责处理特定的条件。例如:
public double calculateFee(Bill bill) {
double fee = 0;
if (bill.getPlan() == PlanType.A) {
fee = calculatePlanAFee(bill);
} else if (bill.getPlan() == PlanType.B) {
fee = calculatePlanBFee(bill);
}
return fee;
}
private double calculatePlanAFee(Bill bill) {
if (bill.getUsage() > 100) {
return 100 * bill.getBaseRate() + (bill.getUsage() - 100) * bill.getOverRate();
} else {
return bill.getUsage() * bill.getBaseRate();
}
}
private double calculatePlanBFee(Bill bill) {
double fee = 0;
if (bill.getUsage() > 200) {
fee = 200 * bill.getBaseRate() + (bill.getUsage() - 200) * bill.getOverRate();
} else {
fee = bill.getUsage() * bill.getBaseRate();
}
if (bill.getAccounts() > 1) {
fee -= fee * 0.05;
}
return fee;
}
在上面的代码中,首先将嵌套的 if-else 语句分解为两个独立的语句,每个语句负责处理特定的条件。然后,将每个条件计算的代码封装到一个单独的方法中,以提高代码的可读性和可维护性。最后,在 calculateFee 方法中根据用户的费用类型调用不同的方法进行计算,并返回计算结果。
使用 “Decompose Conditional” 重构方法,可以将复杂的条件语句分解为多个简单的条件语句,从而提高代码的可读性和可维护性。
Consolidate Conditional Expression (合并条件表达式)
它的主要目的是将几个条件表达式合并为一个更简单的表达式。
举个例子,假设有一个计算商品折扣的方法 calculateDiscount,其中包含多个条件表达式来判断不同的折扣类型,如下所示:
public double calculateDiscount(double price, int quantity) {
double discount = 0;
if (price > 1000 && quantity > 10) {
discount = price * 0.2;
}
if (price > 500 && quantity > 5) {
discount = price * 0.1;
}
if (price > 100) {
discount = price * 0.05;
}
return discount;
}
这段代码使用了多个条件表达式来判断不同的折扣类型,并根据不同的条件计算出折扣。如果有更多的折扣类型需要添加,那么这段代码会变得非常冗长和难以维护。
为了解决这些问题,可以使用 “Consolidate Conditional Expression” 重构方法。具体实现方式是将多个条件表达式合并为一个更简单的表达式。例如:
public double calculateDiscount(double price, int quantity) {
double discount = 0;
if (price > 1000 && quantity > 10) {
discount = price * 0.2;
} else if (price > 500 && quantity > 5) {
discount = price * 0.1;
} else if (price > 100) {
discount = price * 0.05;
}
return discount;
}
在上面的代码中,将多个条件表达式合并为一个更简单的表达式,使用 else-if 语句进行处理。这样可以使代码更简洁、可读性更高,也更易于维护和扩展。
“Consolidate Conditional Expression” 是一种非常有用的重构方法,它能够将多个复杂的条件表达式合并为一个更简单的表达式,从而提高代码的可读性和可维护性。
Consolidate Duplicate Conditional Fragments (合并重复的条件片段)
它的主要目的是将重复的代码片段合并为一个更简单的形式。
举个例子,假设有一个方法 checkAge,用于检查用户的年龄是否符合要求。如果用户年龄小于 18 岁,那么该方法会返回 false,如果用户年龄大于等于 18 岁但小于 21 岁,那么该方法会弹出一个确认对话框,询问用户是否已经获得了父母或监护人的许可,如果用户确认获得了许可,则返回 true,否则返回 false。最后,如果用户年龄大于等于 21 岁,则直接返回 true。
原始实现中,checkAge 方法中包含了一些重复的代码,如下所示:
public boolean checkAge(int age) {
if (age < 18) {
return false;
} else if (age >= 18 && age < 21) {
boolean hasPermission = showPermissionDialog();
if (hasPermission) {
return true;
} else {
return false;
}
} else {
return true;
}
}
private boolean showPermissionDialog() {
// 弹出确认对话框,询问用户是否获得了父母或监护人的许可
// 如果用户确认获得了许可,则返回 true,否则返回 false
}
在上面的代码中,checkAge 方法中包含了一些重复的代码。在第一个 if 分支和最后一个 else 分支中都只是返回了 true 或 false,这两个分支的代码是完全一样的。为了避免这种重复,可以使用 “Consolidate Duplicate Conditional Fragments” 重构方法,将这些重复的片段合并为一个更简洁的形式。
重构后的代码如下所示:
public boolean checkAge(int age) {
boolean hasPermission = true;
if (age >= 18 && age < 21) {
// 弹出确认对话框,询问用户是否获得了父母或监护人的许可
// 如果用户未确认获得了许可,则 hasPermission 设为 false
hasPermission = showPermissionDialog();
}
return age >= 18 || hasPermission;
}
private boolean showPermissionDialog() {
// 弹出确认对话框,询问用户是否获得了父母或监护人的许可
// 如果用户确认获得了许可,则返回 true,否则返回 false
}
在上面的代码中,我们将重复的代码片段合并为了一个更简洁的形式。我们首先将变量 hasPermission 初始化为 true,然后在第二个条件分支中调用 showPermissionDialog 方法,根据用户的选择设置 hasPermission 的值,最后返回一个判断表达式,根据年龄和 hasPermission 来判断该用户是否符合要求。
通过使用 “Consolidate Duplicate Conditional Fragments” 重构方法,我们可以将重复的代码片段合并为一个更简洁的形式,提高代码的可读性和可维护性。
Remove Control Flag (移除控制标记)
它的主要目的是通过优化循环中的控制标记,来使代码更加清晰和易于理解。
举个例子,假设有一个方法 checkArray,用于检查数组中是否包含某个特定值。该方法使用了一个名为 found 的 boolean 变量,用于记录是否找到了该特定值。原始实现中,checkArray 方法使用了 for 循环来遍历数组,如果找到了该特定值,则将 found 设为 true,并跳出循环。
public boolean checkArray(int[] array, int value) {
boolean found = false;
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
found = true;
break;
}
}
return found;
}
在上面的代码中,使用了 found 变量来记录是否找到了该特定值。这种实现方式虽然可行,但是 found 变量仅仅被用来控制循环流程,而并未真正产生意义,从而降低了代码的可读性。
为了解决这些问题,可以使用 “Remove Control Flag” 重构方法。具体实现方式是使用 break 或 return 语句直接退出循环,而不是使用控制标记。例如:
public boolean checkArray(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
在上面的代码中,我们将控制标记 found 直接更改为使用返回值,如果找到了该特定值,则直接返回 true,否则返回 false。这种实现方式不仅减少了代码的复杂度,而且使代码更加简洁、清晰和易于理解。
通过使用 “Remove Control Flag” 重构方法,我们可以优化循环中的控制标记,使代码更加简洁、清晰和易于理解。同时,这种实现方式还可以提高代码的可读性和可维护性。
Replace Nested Conditional with Guard Clauses (以卫语句取代嵌套条件表达式)
它的主要目的是通过使用卫语句来取代嵌套条件表达式,从而使得代码更加清晰和易于理解。
举个例子,假设有一个方法 calculatePrice,用于计算某个商品的价格。该方法接收两个参数:basePrice,表示该商品的基本价格;season,表示当前季节。原始实现中,calculatePrice 方法使用了多个嵌套的条件语句来根据不同的季节计算商品的价格。具体实现如下:
public double calculatePrice(double basePrice, String season) {
double price = 0.0;
if (season.equals("春季")) {
price = basePrice * 1.2;
} else {
if (season.equals("夏季")) {
price = basePrice * 1.5;
} else {
if (season.equals("秋季")) {
price = basePrice * 1.0;
} else {
if (season.equals("冬季")) {
price = basePrice * 1.8;
}
}
}
}
return price;
}
在上面的代码中,我们可以看到,使用了嵌套的 if-else 语句来判断不同的季节,并计算商品的价格。这种实现方式虽然可行,但是会使得代码变得复杂和难以理解。
为了解决这些问题,可以使用 “Replace Nested Conditional with Guard Clauses” 重构方法。具体实现方式是使用卫语句来避免嵌套条件表达式。例如:
public double calculatePrice(double basePrice, String season) {
if (season.equals("春季")) {
return basePrice * 1.2;
}
if (season.equals("夏季")) {
return basePrice * 1.5;
}
if (season.equals("秋季")) {
return basePrice * 1.0;
}
if (season.equals("冬季")) {
return basePrice * 1.8;
}
return 0.0;
}
在上面的代码中,我们使用了多个卫语句来代替嵌套的条件表达式,如果当前季节匹配成功,则直接返回相应的价格,否则返回默认值 0.0。这种实现方式不仅使得代码更加简洁和易于理解,而且减少了代码的复杂度,提高了代码的可读性和可维护性。
通过使用 “Replace Nested Conditional with Guard Clauses” 重构方法,我们可以使用卫语句来取代嵌套条件表达式,使得代码更加简洁、清晰和易于理解。同时,这种实现方式还可以提高代码的可读性和可维护性,减少代码的复杂度。
Replace Conditional with Polymorphism (以多态取代条件表达式)
它的主要目的是通过使用多态来取代条件表达式,从而使得代码更加清晰和易于理解。
举个例子,假设有一个程序用于处理不同类型的几何图形,并计算它们的面积。该程序包含一个方法 calculateArea,用于计算某个几何图形的面积,该方法接收一个图形对象作为参数。原始实现中,calculateArea 方法使用了多个条件表达式来判断图形对象的类型,并根据不同的类型来计算面积。具体实现如下:
public double calculateArea(Shape shape) {
if (shape instanceof Rectangle) {
Rectangle rectangle = (Rectangle) shape;
return rectangle.getWidth() * rectangle.getHeight();
} else {
if (shape instanceof Circle) {
Circle circle = (Circle) shape;
return Math.PI * circle.getRadius() * circle.getRadius();
} else {
throw new IllegalArgumentException("Unsupported shape type");
}
}
}
在上面的代码中,我们可以看到,使用了多个条件表达式来判断图形对象的类型,并根据不同的类型来计算面积。这种实现方式虽然可行,但是会使得代码变得复杂和难以维护。
为了解决这些问题,可以使用 “Replace Conditional with Polymorphism” 重构方法。具体实现方式是使用多态来避免条件表达式。例如:
public abstract class Shape {
public abstract double calculateArea();
}
public class Rectangle extends Shape {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double calculateArea() {
return width * height;
}
}
public class Circle extends Shape {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double calculateArea() {
return Math.PI * radius * radius;
}
}
在上面的代码中,我们定义了一个抽象类 Shape,并在其中定义了一个抽象方法 calculateArea,用于计算图形的面积。然后我们分别创建了两个子类 Rectangle 和 Circle,分别用于表示矩形和圆形,并实现了 calculateArea 方法。在客户端代码中,我们只需要传递一个具体的 Shape 对象,然后调用 calculateArea 方法即可计算出相应的面积。
通过使用 “Replace Conditional with Polymorphism” 重构方法,我们可以使用多态来取代条件表达式,使得代码更加简洁、清晰和易于理解。同时,这种实现方式还可以提高代码的可扩展性和可维护性,减少代码的复杂度,并避免了大量的类型判断和强制类型转换。
Introduce Null Object (引入 Null 对象)
它的主要目的是通过引入一个 “Null Object” 来取代空引用,从而使得代码更加健壮和易于维护。
举个例子,假设有一个程序用于处理文件系统中的文件,其中包含一个类 FileSystem,用于表示某个文件或目录。该类包含一个方法 getSize,用于计算文件或目录的大小。原始实现中,getSize 方法返回一个空引用(null),表示该文件或目录不存在。这样的实现方式虽然可行,但是容易导致空指针异常,需要在客户端代码中添加大量的判断语句来避免异常的发生。具体实现如下:
public class FileSystem {
private String name;
public FileSystem(String name) {
this.name = name;
}
public long getSize() {
// 模拟计算文件或目录的大小
if (fileExists()) {
return 100L;
} else {
return null; // 返回空引用(null)
}
}
private boolean fileExists() {
// 模拟判断文件或目录是否存在
return false;
}
}
在上面的代码中,我们可以看到,getSize 方法返回一个空引用(null),表示该文件或目录不存在。这种实现方式容易导致客户端代码出现空指针异常,需要在客户端代码中添加大量的判断语句来避免异常的发生。
为了解决这些问题,可以使用 “Introduce Null Object” 重构方法。具体实现方式是引入一个 “Null Object”,用于取代空引用。例如:
public interface FileSystem {
long getSize();
}
public class NullFileSystem implements FileSystem {
public long getSize() {
return 0L;
}
}
public class RealFileSystem implements FileSystem {
private String name;
public RealFileSystem(String name) {
this.name = name;
}
public long getSize() {
// 模拟计算文件或目录的大小
if (fileExists()) {
return 100L;
} else {
return 0L;
}
}
private boolean fileExists() {
// 模拟判断文件或目录是否存在
return false;
}
}
在上面的代码中,我们定义了一个接口 FileSystem,并在其中定义了一个 getSize 方法,用于计算文件或目录的大小。然后我们分别创建了两个实现类 NullFileSystem 和 RealFileSystem,分别用于表示空对象和真实对象,并实现了 getSize 方法。在客户端代码中,我们只需要传递一个具体的 FileSystem 对象,然后调用 getSize 方法即可计算出相应的大小。
通过使用 “Introduce Null Object” 重构方法,我们可以引入一个 “Null Object” 来取代空引用,使得代码更加健壮和易于维护。同时,这种实现方式还能够提高代码的可扩展性和可维护性,减少客户端代码的复杂度,并避免了空指针异常的发生。
Introduce Assertion (引入断言)
它的主要目的是在代码中添加断言,用于检查程序的正确性和稳定性。
举个例子,假设有一个程序用于处理银行账户,并包含一个类 BankAccount,用于表示某个用户的账户信息。该类包含一个方法 deposit,用于存款。原始实现中,deposit 方法只是简单地将存款金额加到当前余额上,没有进行任何检查和验证。这样的实现方式容易导致存款后余额为负数,需要在客户端代码中添加大量的判断语句来避免异常的发生。具体实现如下:
public class BankAccount {
private double balance;
public void deposit(double amount) {
balance += amount; // 存款
}
public double getBalance() {
return balance;
}
}
在上面的代码中,我们可以看到,deposit 方法只是简单地将存款金额加到当前余额上,没有进行任何检查和验证。这种实现方式容易导致存款后余额为负数,需要在客户端代码中添加大量的判断语句来避免异常的发生。
为了解决这些问题,可以使用 “Introduce Assertion” 重构方法。具体实现方式是在代码中添加断言,用于检查程序的正确性和稳定性。例如:
public class BankAccount {
private double balance;
public void deposit(double amount) {
assert amount > 0; // 断言存款金额必须大于零
balance += amount; // 存款
assert balance >= 0; // 断言存款后余额必须大于等于零
}
public double getBalance() {
return balance;
}
}
在上面的代码中,我们使用两个断言来检查存款金额和存款后余额是否符合要求。如果不符合要求,则会抛出 AssertionError 异常。在客户端代码中,我们可以根据需要启用或禁用断言。
通过使用 “Introduce Assertion” 重构方法,我们可以在代码中添加断言,用于检查程序的正确性和稳定性。这种实现方式能够提高代码的可靠性和稳定性,并减少客户端代码的复杂度。同时,这种实现方式也能够提供更好的调试和测试支持。