Remove the full screen if statement to make the code clean and refreshing

When you see the full screen of if, do you feel dizzy and doubtful about life? In fact, when we write Java code, there are several ways to reduce the use of if statements and improve the readability and maintainability of the code. Several common methods are listed below:

  1. Use polymorphism: By using object-oriented polymorphism, different logic can be encapsulated into different classes, avoiding a large number of if
    statements. Use inheritance and interfaces to define common methods and have concrete implementing classes implement those methods.
  2. Use design patterns: Use design patterns to better organize and manage code logic and reduce
    the use of if statements. For example, Factory Pattern, Strategy Pattern, Observer Pattern, etc. can all help transform complex conditional logic into clearer and extensible structures.
  3. Use a mapping table: Put conditions and corresponding operations into a mapping table, avoiding the use of a large number of if-else statements. Map
    or other data structures can be used to store the relationship between conditions and corresponding operations, and the corresponding operations can be obtained by looking up the mapping table.
  4. Use the strategy pattern: The strategy pattern encapsulates different strategies into independent classes, avoiding the use of a large number of conditional statements. By defining a strategy interface, and then implementing different strategy classes for different situations, select the appropriate strategy for processing as needed.
  5. Use enumeration: Use enumeration to encapsulate conditions and corresponding operations into enumeration constants, avoiding the use of a large number of if-else
    statements. Each enumeration constant represents a situation, has a corresponding method or behavior, and performs corresponding operations through the enumeration constant.
  6. Use functional programming: Functional programming features introduced in Java 8 can help reduce conditional judgments. By using
    functional programming features such as lambda expressions and method references, logic can be encapsulated into functions and passed, avoiding a large number of if-else statements.

Of course, there are several less common methods:

  1. Use assertions (Assertions): By using assertions to ensure that certain conditions are met, you can avoid using a lot of conditional judgments. Assertions are usually used during the development and debugging phases. Assertion statements can be inserted into the code to verify preconditions, and an exception will be thrown if the conditions are not met.
  2. Use the state pattern: The state pattern allows an object to change its behavior when its internal state changes, thus avoiding a lot of conditional judgments. By defining different state classes and maintaining the current state inside the object, you can perform corresponding operations according to the state without using a large number of
    if-else statements.
  3. Use a rule engine: A rule engine is a rule-based programming model. By defining conditions and corresponding operations as rules, a large number of conditional judgments can be avoided. Rules engines provide a declarative way to describe and execute conditional logic, making code more readable, extensible, and maintainable.
  4. Use function overloading: Function overloading allows multiple methods with the same name but different parameter lists to be defined in the same class. By using function overloading, different methods can be called according to different parameter types or numbers without using conditional judgment to determine specific operations.
  5. Use an expression language: In some cases, you can use an expression language to handle conditional logic and avoid using a lot of conditional judgments. For example, when using a rules engine or dynamic queries, you can use the expression language to define and execute conditional expressions.

Here is a sample code using polymorphism:

abstract class Shape {
    abstract void draw();
}

class Rectangle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a rectangle");
    }
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a circle");
    }
}

class Triangle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a triangle");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        Shape circle = new Circle();
        Shape triangle = new Triangle();

        drawShape(rectangle); // 调用多态方法,输出:Drawing a rectangle
        drawShape(circle);    // 调用多态方法,输出:Drawing a circle
        drawShape(triangle);  // 调用多态方法,输出:Drawing a triangle
    }

    static void drawShape(Shape shape) {
        shape.draw();
    }
}


In the above example, we defined an abstract class Shape, which has an abstract method draw(). Then we created three concrete subclasses of Rectangle, Circle and Triangle, which inherited from Shape and implemented the draw() method respectively.

In the Main class, we define a static method drawShape() whose parameter is of type Shape. This method utilizes polymorphism, can accept any subclass object as a parameter, and calls the corresponding draw() method. In this way, we can draw different shapes by calling the drawShape() method without using a lot of if statements to determine the specific shape type.

By using polymorphism, we treat specific shape objects as Shape types, and dynamically call the corresponding draw() method according to the actual object type. This avoids a lot of if statements, making the code more concise, extensible, and maintainable.

The following are two commonly used design pattern sample codes, namely the strategy pattern and the factory pattern:

Strategy pattern sample code:

interface PaymentStrategy {
    void pay(double amount);
}

class CreditCardPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("Paid " + amount + " using Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("Paid " + amount + " using PayPal");
    }
}

class PaymentContext {
    private PaymentStrategy paymentStrategy;

    public PaymentContext(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void performPayment(double amount) {
        paymentStrategy.pay(amount);
    }
}

public class Main {
    public static void main(String[] args) {
        PaymentContext paymentContext1 = new PaymentContext(new CreditCardPayment());
        paymentContext1.performPayment(100.0); // 输出:Paid 100.0 using Credit Card

        PaymentContext paymentContext2 = new PaymentContext(new PayPalPayment());
        paymentContext2.performPayment(50.0); // 输出:Paid 50.0 using PayPal
    }
}

In the above example, a payment strategy interface PaymentStrategy is defined, and there are two implementation classes CreditCardPayment and PayPalPayment, which represent payment by credit card and PayPal respectively.

The PaymentContext class is the context of the strategy pattern, holds a payment strategy object, and injects a specific payment strategy through the constructor. The performPayment() method is used to perform the payment operation, and what is actually called is the pay() method of the specific payment strategy object.

By using the strategy pattern, we encapsulate different payment methods into different strategy classes, instead of judging the specific payment method through a large number of if statements. By passing the payment strategy object to the context object, the function of dynamically selecting and executing the payment strategy is realized.

Factory pattern sample code:

interface Shape {
    void draw();
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class ShapeFactory {
    public static Shape createShape(String shapeType) {
        if (shapeType.equalsIgnoreCase("rectangle")) {
            return new Rectangle();
        } else if (shapeType.equalsIgnoreCase("circle")) {
            return new Circle();
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape rectangle = ShapeFactory.createShape("rectangle");
        if (rectangle != null) {
            rectangle.draw(); // 输出:Drawing a rectangle
        }

        Shape circle = ShapeFactory.createShape("circle");
        if (circle != null) {
            circle.draw(); // 输出:Drawing a circle
        }
    }
}

In the above example, a shape interface Shape is defined, and there are two implementation classes Rectangle and Circle, representing

Here is a sample code to solve the if problem using a map:

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // 创建映射表,将条件和对应的处理逻辑关联起来
        Map<String, Runnable> actionMap = new HashMap<>();
        actionMap.put("condition1", () -> {
            // 处理条件1的逻辑
            System.out.println("Condition 1 is met");
        });
        actionMap.put("condition2", () -> {
            // 处理条件2的逻辑
            System.out.println("Condition 2 is met");
        });

        // 模拟根据条件选择处理逻辑
        String condition = "condition2";
        if (actionMap.containsKey(condition)) {
            // 根据条件获取对应的处理逻辑,并执行
            actionMap.get(condition).run();
        } else {
            // 处理无效条件
            System.out.println("Invalid condition: " + condition);
        }
    }
}

In the above example, we created an actionMap mapping table to associate different conditions with corresponding processing logic. The Runnable functional interface is used here to define logical operations.

By calling the put() method, we add different conditions and corresponding processing logic to the mapping table. In the main program, we specify the conditions to be processed.

Then, we judge whether the specified condition is contained in the mapping table, and if so, obtain the corresponding processing logic from the mapping table, and execute the logic by calling the run() method. If the specified condition is not included in the mapping table, a prompt message of invalid condition will be output.

Use enumeration to solve if problem Specific sample code

Using enumeration can effectively solve the if problem, the following is a sample code that uses enumeration to solve the if problem:

enum Condition {
    CONDITION_1,
    CONDITION_2
}

interface Action {
    void execute();
}

class Condition1Action implements Action {
    @Override
    public void execute() {
        // 处理条件1的逻辑
        System.out.println("Condition 1 is met");
    }
}

class Condition2Action implements Action {
    @Override
    public void execute() {
        // 处理条件2的逻辑
        System.out.println("Condition 2 is met");
    }
}

public class Main {
    public static void main(String[] args) {
        Condition condition = Condition.CONDITION_2;

        switch (condition) {
            case CONDITION_1:
                new Condition1Action().execute();
                break;
            case CONDITION_2:
                new Condition2Action().execute();
                break;
            default:
                System.out.println("Invalid condition: " + condition);
        }
    }
}

In the above example, we defined an enumeration Condition, which contains different condition values. Next, we created an Action interface to define the execution method of the processing logic.

Then, we define the specific implementation classes Condition1Action and Condition2Action, which correspond to the processing logic of different conditions.

In the main program, we specify the conditions to be processed. By using the switch statement, select the corresponding processing logic according to the enumeration value of the condition, and execute it.

Enumeration can be used to encapsulate different conditions and corresponding processing logic, avoiding the use of a large number of if statements. Enums provide a more intuitive and readable way to express conditions, and make it easy to extend and maintain your code

Using an enumeration with a switch statement has the following differences compared to using an if statement:

  • Readability: Using enums can improve the readability of your code. Enumeration constants have clear semantics and can visually represent different conditions. In the switch
    statement, different conditions are distinguished by enumerating constants, and the code logic is clearer.
  • Extensibility: Using a combination of enumerations and switch statements makes it easy to extend and maintain the code. If you need to add a new condition, you only need to add a new constant in the enumeration, and add the corresponding logic processing in the switch statement, without modifying other parts of the code.
  • Type safety: enumeration constants are type-safe, and the compiler will check whether the enumeration constants are all processed in the switch statement, so as to avoid missing conditions. When using the if statement, there may be omissions or repetitions in the conditional judgment.
  • Performance: In some cases, using a switch statement may be more efficient than using a series of if-else statements, because the switch statement can be optimized. However, the performance impact varies from case to case, depending on the compiler and runtime environment.

Use functional programming to solve the if problem, specific sample code

The use of functional programming can simplify the processing of conditional judgments. The following is a sample code that uses functional programming to solve the if problem:

import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

public class Main {
    public static void main(String[] args) {
        Map<String, Consumer<String>> actionMap = new HashMap<>();
        actionMap.put("condition1", Main::handleCondition1);
        actionMap.put("condition2", Main::handleCondition2);

        String condition = "condition2";
        actionMap.getOrDefault(condition, Main::handleInvalidCondition).accept(condition);
    }

    private static void handleCondition1(String condition) {
        // 处理条件1的逻辑
        System.out.println("Condition 1 is met");
    }

    private static void handleCondition2(String condition) {
        // 处理条件2的逻辑
        System.out.println("Condition 2 is met");
    }

    private static void handleInvalidCondition(String condition) {
        // 处理无效条件
        System.out.println("Invalid condition: " + condition);
    }
}

In the above example, we use the java.util.function.Consumer functional interface to represent the processing logic corresponding to the condition. The conditions are associated with the corresponding processing logic through the Map data structure.

In the main program, we specify the conditions to be processed. By calling the getOrDefault() method, the corresponding processing logic is obtained according to the condition (use method references Main::handleCondition1 and Main::handleCondition2), and if the condition does not exist in the mapping table, use Main::handleInvalidCondition to handle the invalid condition.

By calling the accept() method, the condition is passed to the corresponding processing logic, thereby realizing the processing of the conditional judgment.

Using functional programming, the logic of conditional judgment can be encapsulated in the function interface, and the code can be more concise and easy to maintain through the combination and transfer of functions. Functional programming provides a declarative way to handle conditional judgments, rather than through a large number of if-else statements to handle different situations.

Guess you like

Origin blog.csdn.net/yuanchengfu0910/article/details/130930128