重构:Java 代码中消除 If-Else 语句以获得更清晰、可扩展的逻辑


尽管 if-else 语句无处不在,但如果过度使用,可能会导致代码复杂且难以维护。在本文中,我们将探索各种策略来减少在项目中 if-else 结构的使用,重点是使代码更加模块化、可维护和可读。

减少 If-Else 语句的策略

  • 策略模式

  • 使用枚举

  • 多态性

  • Lambda表达式和函数式接口

  • 命令模式

  • 保护子句

让我们通过示例深入了解每种策略。

策略模式

策略模式定义了一系列算法,封装了每个算法,并使它们可以互换。当您有多种方法来执行某项任务时,此模式非常有用。

示例:支付处理系统

首先,定义PaymentStrategy接口:

public interface PaymentStrategy {
		public void pay(double amount);
}

接下来,实现不同的支付策略:

public class AliPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // ali支付处理逻辑
        System.out.println("Paid " + amount + " using Ali.");
    }
}

public class WeChatPayment implements PaymentStrategy {
    @Override
    public void pay(double amount) {
        // wechat支付处理逻辑
        System.out.println("Paid " + amount + " using WeChat.");
    }
}

创建一个使用以下策略的PaymentService:

public class PaymentService {
    private final Map<String, PaymentStrategy> paymentStrategies = new HashMap<>();

    public PaymentService(List<PaymentStrategy> strategies) {
        for (PaymentStrategy strategy : strategies) {
          	paymentStrategies.put(strategy.getClass().getSimpleName(), strategy);
        }
    }

    public void processPayment(String strategyName, double amount) {
        PaymentStrategy strategy = paymentStrategies.get(strategyName);
        if (strategy != null) {
          	strategy.pay(amount);
        } else {
          	throw new IllegalArgumentException("No such payment strategy: " + strategyName);
        }
    }
}

最后,测试使用:

public class PaymentTest {
    public static void main(String[] args) {
        // 没有PaymentService 的使用
        String payMethod = "WeChatPayment";
        if ("WeChatPayment".equals(payMethod)) {
          	new WeChatPayment().pay(100);
        } else if ("AliPayment".equals(payMethod)) {
          	new AliPayment().pay(100);
        }
        // other else if ...

        // 加入PaymentService来消除if..else
        List<PaymentStrategy> paymentStrategies = Arrays.asList(new AliPayment(), new WeChatPayment());
        PaymentService paymentService = new PaymentService(paymentStrategies);
        paymentService.processPayment("WeChatPayment", 100);
    }
}

// output: Paid 100.0 using WeChat.

使用枚举

枚举可以用来表示一组预定义的常量及其相关行为。

示例:订单状态管理

定义一个具有不同行为的OrderStatus枚举:

public enum OrderStatus {
    NEW {
        @Override
        public void handle() {
          	System.out.println("Processing new order.");
        }
    },
    SHIPPED {
        @Override
        public void handle() {
          	System.out.println("Order shipped.");
        }
    },
    DELIVERED {
        @Override
        public void handle() {
          	System.out.println("Order delivered.");
        }
    };

    public abstract void handle();
}

在服务中使用此枚举:

public class OrderService {
    public void processOrder(OrderStatus status) {
      	status.handle();
    }
}

最后,测试使用:

public class OrderTest {
    public static void main(String[] args) {
        OrderStatus orderStatus = OrderStatus.NEW;
        // 之前的使用
        if (orderStatus == OrderStatus.NEW) {
          	System.out.println("Processing new order.");
        } else if (orderStatus == OrderStatus.SHIPPED) {
          	System.out.println("Order shipped.");
        } else if (orderStatus == OrderStatus.DELIVERED) {
          	System.out.println("Order delivered.");
        }

        // 之后的使用
        OrderService orderService = new OrderService();
        orderService.processOrder(orderStatus);
    }
}


// output: Processing new order.

多态性

多态性允许将对象视为其父类的实例,而不是其实际类的实例。这使您能够通过父类的引用调用派生类的重写方法。

示例:通知系统

定义一个Notification接口及其实现:

public interface Notification {
		void send(String message);
}

public class EmailNotification implements Notification {
    @Override
    public void send(String message) {
        // Email sending logic
        System.out.println("Sending email: " + message);
    }
}

public class SmsNotification implements Notification {
    @Override
    public void send(String message) {
        // SMS sending logic
        System.out.println("Sending SMS: " + message);
    }
}

创建一个使用多态性的服务:

public class NotificationService {

    private final List<Notification> notifications;

    public NotificationService(List<Notification> notifications) {
      	this.notifications = notifications;
    }

    public void notifyAll(String message) {
        for (Notification notification : notifications) {
          notification.send(message);
        }
    }
}

最后,测试使用:

public class MyTest {
    public static void main(String[] args) {
        List<Notification> notifications = Arrays.asList(new EmailNotification(), new SmsNotification());
        NotificationService notificationService = new NotificationService(notifications);
        notificationService.notifyAll("hello word");
    }
}

// output: 
// Sending email: hello word
// Sending SMS: hello word

Lambda 表达式和函数式接口

Lambda 表达式可以简化您的代码,尤其是在处理小型、单一方法接口时。

示例:折扣服务

public class DiscountService {
    private Map<String, Function<Double, Double>> discountStrategies = new HashMap<>();

    public DiscountService() {
        discountStrategies.put("SUMMER_SALE", (price) -> price * 0.9);
        discountStrategies.put("SUMMER_SALE", (price) -> price * 0.8);
    }

    public double applyDiscount(String discountCode, double price) {
      	return discountStrategies.getOrDefault(discountCode, Function.identity()).apply(price);
    }
}

最后,测试使用:

public class MyTest {
    public static void main(String[] args) {
        DiscountService discountService = new DiscountService();

        double sale = discountService.applyDiscount("SUMMER_SALE", 100);

        System.out.println("sale: " + sale);
    }
}

// output: sale: 80.0

命令模式

命令模式将请求封装为对象,从而允许您使用队列、请求和操作对客户端进行参数化。

示例:文件操作

定义Command接口和具体命令:

public interface Command {
		void execute();
}

public class OpenFileCommand implements Command {
    private FileSystemReceiver fileSystemReceiver;

    public OpenFileCommand(FileSystemReceiver fs) {
      	this.fileSystemReceiver = fs;
    }

    @Override
    public void execute() {
      	this.fileSystemReceiver.openFile();
    }
}

public class CloseFileCommand implements Command {
    private FileSystemReceiver fileSystemReceiver;

    public CloseFileCommand(FileSystemReceiver fs) {
      	this.fileSystemReceiver = fs;
    }

    @Override
    public void execute() {
      	this.fileSystemReceiver.closeFile();
    }
}

定义文件系统接收器:

public interface FileSystemReceiver {
    void openFile();
    void closeFile();
}

public class UnixFileSystemReceiver implements FileSystemReceiver {
    @Override
    public void openFile() {
      	System.out.println("Opening file in Unix OS");
    }

    @Override
    public void closeFile() {
      	System.out.println("Closing file in Unix OS");
    }
}

public class WindowsFileSystemReceiver implements FileSystemReceiver {
    @Override
    public void openFile() {
      	System.out.println("Opening file in Windows OS");
    }

    @Override
    public void closeFile() {
      	System.out.println("Closing file in Windows OS");
    }
}

定义文件调用器:

public class FileInvoker {

    private Command command;

    public FileInvoker(Command command) {
      	this.command = command;
    }

    public void execute() {
      	this.command.execute();
    }
}

最后,测试使用:

public class MyTest {
    public static void main(String[] args) {
        FileInvoker fileInvoker = new FileInvoker(new OpenFileCommand(new UnixFileSystemReceiver()));
        fileInvoker.execute();
    }
}

// output: Opening file in Unix OS

保护子句

思路其实就是,让出错的代码先返回,前面把所有的错误判断全判断掉,然后就剩下的就是正常的代码了

示例:用户验证

不要嵌套 if-else 语句来验证用户输入,而是使用 guard 子句来预先处理无效情况:

public class UserService {
    public void registerUser(User user) {
        if (user == null) {
            throw new IllegalArgumentException("User cannot be null");
        }
        if (user.getName() == null || user.getName().isEmpty()) {
            throw new IllegalArgumentException("User name cannot be empty");
        }
        if (user.getEmail() == null || user.getEmail().isEmpty()) {
            throw new IllegalArgumentException("User email cannot be empty");
        }
        // Proceed with registration
        System.out.println("Registering user: " + user.getName());
    }
}

这种方法可以确保及早处理无效条件,使主要逻辑保持清晰。

抽取成函数

有人说:“如果代码不共享,就不要抽取成函数!”,持有这个观点的人太死读书了。函数是代码的封装或是抽象,并不一定用来作代码共享使用,函数用于屏蔽细节,让其它代码耦合于接口而不是细节实现,这会让我们的代码更为简单,简单的东西都能让人易读也易维护。这才是函数的作用。

参考


文章作者: 张权
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 张权 !
评论
  目录