您现在的位置是:首页 >技术杂谈 >Java代码重构学习笔记-简化条件表达式网站首页技术杂谈

Java代码重构学习笔记-简化条件表达式

Logan_addoil 2024-06-14 18:01:02
简介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” 重构方法,我们可以在代码中添加断言,用于检查程序的正确性和稳定性。这种实现方式能够提高代码的可靠性和稳定性,并减少客户端代码的复杂度。同时,这种实现方式也能够提供更好的调试和测试支持。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。