優雅重構if-else的12種方法
環境:Java21
1. 簡介
在軟件開發中,過度依賴if-else會導致代碼難以維護、擴展性差,形成所謂的“面條代碼”。隨著業務邏輯復雜化,嵌套條件判斷更會顯著降低可讀性與測試效率。
本篇文章匯總16+種經過實踐驗證的優雅重構方法,助你寫出更清晰、可維護的代碼。
2.實戰案例
2.1 刪除不必要的else語句
有些時候你會發現else語句塊根本沒有必要,如下示例:
public void remove_unnecessary_else_example(Integer input) {
if (input > 10) {
// ...
} else {
// TODO else
}
}優化后
public void remove_unnecessary_else_solution(Integer input) {
if (input > 10) {
//do sth
return;
}
// TODO
}2.2 三元運算符
三元運算符的使用可以簡化某些if-else結構,使代碼更簡潔易讀,如下示例:
public Integer calc(Integer input) {
if (input > 10) {
return 50;
} else {
return 100;
}
}優化后
public Integer calc(Integer input) {
return input>10 ? 50 : 100;
}2.3 合并條件表達式
如果存在一系列返回相同結果的條件,可以將它們合并為單個條件表達式,從而使邏輯更清晰,如下示例:
public Integer calc(Player input) {
if(input.getAge() <18) {
return 5;
}
if (input.getHeight() < 1.80) {
return 5;
}
if (input.getWeight() < 50) {
return 5;
}
return 0;
}優化后
public Integer merge_conditional_expression_solution(Player input) {
if(input.getAge() <18 || input.getHeight() < 1.80 || input.getWeight() < 50) {
return 5;
}
return 0;
}2.4 使用switch-case
switch-case 語句可用于處理多種條件,且代碼更簡潔易讀,如下示例:
public Integer switch_example(Integer input) {
if (input == 1) {
//do sth 1
}
if(input == 2) {
//do sth 2
}
if(input ==3 ) {
//do sth 3
}
return 0;
}優化后
public Integer switch_solution(Integer input) {
switch (input) {
case 1: //do sth 1
case 2: //do sth 2
case 3: //do sth 3
default: return 0;
}
}
// 或者如下
public Integer switch_solution(Integer input) {
return switch (input) {
case 1 -> {
// do sth 1
System.out.println("Handling case 1");
yield 1; // 使用 yield 返回值
}
case 2 -> {
// do sth 2
System.out.println("Handling case 2");
yield 2;
}
case 3 -> {
// do sth 3
System.out.println("Handling case 3");
yield 3;
}
default -> {
// default handling
System.out.println("Default case");
yield 0;
}
};
}2.5 守護函數
守護函數是一種用于保護其他函數免受無效輸入影響的函數。它通過檢查輸入的有效性,并在輸入無效時拋出異常來實現保護作用。簡而言之,就是“盡早返回”,如下示例:
public Integer calc(Player input) {
if(input != null) {
String name = input.getName();
if(StringUtils.hasText(name)){
if(input.getAge() >18) {
return 60;
}
}
}
}優化后
public Integer calc(Player input) {
if(input == null) {
return 0;
}
if(!StringUtils.hasText(input.getName())){
return 0;
}
if(input.getAge() <=18) {
return 0;
}
return 60;
}2.6 值的分配
當你需要根據提供的某些輸入為變量分配新值時,應停止使用 if-else 語句,轉而采用更易讀的方式。如果我們不使用 else,代碼將變得更加簡潔、清晰,如下示例:
public Integer calc(Integer input) {
if (input == 1) {
return 10;
} else if(input == 2) {
return 20;
} else {
return 0;
}
}優化后
public Integer value_assignment_solution(Integer input) {
if(input == 1) return 10;
if(input == 2) return 20;
return 0;
}2.7 Optional
有時if-else更合適,因為它會導致非空判斷,這種情況下可以使用Optional。如下示例:
public class OptionalWay {
public static void main(String[] args) {
OptionalWay optionalWay = new OptionalWay();
String s = "test";
optionalWay.oldway(s);
optionalWay.newway(s);
}
private void errorHandle() {
log.error("exist a null");
}
private void keepWorking(String s) {
log.info(s);
}
// 優化前
private void oldway(String s) {
if(s != null) {
keepWorking(s);
} else {
errorHandle();
}
}
// 優化后
private void newway(String s) {
Optional<String> strOptional = Optional.of(s);
strOptional.ifPresentOrElse(this::keepWorking, this::errorHandle);
}
}枚舉:在某些情況下,使用枚舉也能優化if-else邏輯分支,如下示例:
public class EnumWay {
public static void main(String[] args) {
EnumWay enumWay = new EnumWay();
enumWay.oldWay(18);
enumWay.newWay(18);
}
// 優化前
private Integer oldWay(int i) {
if(i==18){
return 1;
}else if(i == 20){
return 2;
}else if(i==30){
return 3;
}
return 0;
}
// 優化后
private Integer newWay(int i) {
return AgeValueEnum.of(i).getValue();
}
public enum AgeValueEnum {
YOUND(18,1),
MID(20,2),
OLD(30,3);
private int age;
private int value;
public int getAge() {
return age;
}
public int getValue() {
return value;
}
AgeValueEnum(int age, int value){
this.age = age;
this.value =value;
}
static AgeValueEnum of(int age) {
for (AgeValueEnum temp : AgeValueEnum.values()) {
if (temp.getAge() == age) {
return temp;
}
}
return null;
}
}
}2.8 反射機制
反射機制能夠獲取類和方法的信息,從而動態執行不同的代碼,如下示例:
public class Shape {
public double getArea() {
if (this instanceof Circle) {
return Math.PI * ((Circle) this).getRadius() * ((Circle) this).getRadius();
} else if (this instanceof Square) {
return ((Square) this).getSide() * ((Square) this).getSide();
} else if (this instanceof Triangle) {
return 0.5 * ((Triangle) this).getBase() * ((Triangle) this).getHeight();
} else {
throw new IllegalArgumentException("Unknown shape type!");
}
}
}
public class Circle extends Shape {
private double radius;
// getters,setters,constructors
}
public class Square extends Shape {
private double side;
}
public class Triangle extends Shape {
private double base;
private double height;
// ...
}
public class Main {
public static void main(String[] args) {
Shape circle = new Circle(5);
System.out.println("Circle area: " + circle.getArea());
Shape square = new Square(4);
System.out.println("Square area: " + square.getArea());
Shape triangle = new Triangle(3, 4);
System.out.println("Triangle area: " + triangle.getArea());
}
}優化后
public abstract class Shape {
public abstract double getArea();
public static Shape getShape(String shapeType) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> clazz = Class.forName(shapeType);
return (Shape) clazz.newInstance();
}
}
public class Circle extends Shape {
// ...
@Override
public double getArea() {
return Math.PI * radius * radius;
}
}
public class Square extends Shape {
// ...
@Override
public double getArea() {
return side * side;
}
}
public class Triangle extends Shape {
// ...
@Override
public double getArea() {
return 0.5 * base * height;
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Shape circle = Shape.getShape("Circle");
circle.setRadius(5);
System.out.println("Circle area: " + circle.getArea());
Shape square = Shape.getShape("Square");
square.setSide(4);
System.out.println("Square area: " + square.getArea());
Shape triangle = Shape.getShape("Triangle");
triangle.setBase(3);
triangle.setHeight(4);
System.out.println("Triangle area: " + triangle.getArea());
}
}2.9 多態性
我們可以將某些操作(如不同狀態)的通用方法抽象為一個公共接口,然后為這些操作實現該接口以完成不同的邏輯。在調用時,只需傳入對應的操作類實例,外部的調用方式保持一致,如下示例:
public class Animal {
public void makeSound() {
if (this instanceof Dog) {
System.out.println("woof!");
} else if (this instanceof Cat) {
System.out.println("meow!");
} else if (this instanceof Cow) {
System.out.println("moo!");
} else {
System.out.println("UNKNOWN!");
}
}
}
public class Dog extends Animal {
}
public class Cat extends Animal {
}
public class Cow extends Animal {
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound();
Animal cat = new Cat();
cat.makeSound();
Animal cow = new Cow();
cow.makeSound();
}
}優化后
// 動物抽象類
public abstract class Animal {
public abstract void makeSound();
}
// 狗類
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("woof!");
}
}
// 貓類
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("meow!");
}
}
// 牛類
public class Cow extends Animal {
@Override
public void makeSound() {
System.out.println("moo!");
}
}
// 主程序
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.makeSound(); // 輸出: woof!
Animal cat = new Cat();
cat.makeSound(); // 輸出: meow!
Animal cow = new Cow();
cow.makeSound(); // 輸出: moo!
}
}2.10 策略模式
策略模式(Strategy Pattern)可以將不同的處理邏輯封裝到獨立的策略類中,從而避免使用冗長的 if-else 或 switch-case 條件判斷代碼塊,如下示例:
public class StrategyMain {
public static void main(String[] args) {
oldway(18);
newway(18);
}
private static void oldway(Integer age) {
if(age.equals(18)) {
//do sth young
}else if(age.equals(20)){
//do sth mid
}else if(age.equals(30)){
//do sth old
}else {
//do sth else
}
}
private static void newway(Integer age) {
IAgeService ageService = AgeServiceFactory.getAgeServiceImpl(age);
ageService.value();
}
}優化后
public interface IAgeService {
void value();
}
public class YoungAgeServiceImpl implements IAgeService {
@Override
public void value() {
// do sth young
}
}
public class MidAgeServiceImpl implements IAgeService {
@Override
public void value() {
// do sth mid
}
}
public class OldAgeServiceImpl implements IAgeService {
@Override
public void value() {
// do sth old
}
}
public class AgeServiceFactory {
private static final Map<Integer, IAgeService> map = new HashMap<>();
static {
map.put(18, new YoungAgeServiceImpl());
map.put(20, new MidAgeServiceImpl());
map.put(30, new OldAgeServiceImpl());
}
public static IAgeService getAgeServiceImpl(Integer age) {
return map.get(age);
}
}2.11 責任鏈模式
責任鏈模式是一種重要的設計模式,它能有效減少 if-else 的使用,提高代碼的簡潔性、可擴展性和靈活性。當 if-else 中的條件表達式過于靈活,難以將條件中的數據抽象成表格并進行統一判斷時,就應該將條件的判斷交給各個功能組件。這些組件以鏈式結構串聯起來,形成一個完整的功能,如下示例:
public class FileHandler {
public void handle(File file) {
if (file instanceof TextFile) {
System.out.println("Handle text file");
} else if (file instanceof ImageFile) {
System.out.println("Handle image file");
} else if (file instanceof VideoFile) {
System.out.println("Handle video file");
} else {
throw new IllegalArgumentException("Unknown file type!");
}
}
}
public class File {
// 基類可以保持為空或添加通用文件屬性/方法
}
public class TextFile extends File {
}
public class ImageFile extends File {
}
public class VideoFile extends File {
}
// 添加UnknownFile類定義(假設這是另一個文件類型)
public class UnknownFile extends File {
}
public class Main {
public static void main(String[] args) {
FileHandler fileHandler = new FileHandler();
TextFile textFile = new TextFile();
fileHandler.handle(textFile);
ImageFile imageFile = new ImageFile();
fileHandler.handle(imageFile);
VideoFile videoFile = new VideoFile();
fileHandler.handle(videoFile);
// 這會拋出IllegalArgumentException
UnknownFile unknownFile = new UnknownFile();
fileHandler.handle(unknownFile);
}
}優化后
public interface Handler {
void handle(File file);
void setNextHandler(Handler nextHandler);
}
public class TextFileHandler implements Handler {
private Handler nextHandler;
@Override
public void handle(File file) {
if (file instanceof TextFile) {
System.out.println("Handle text file");
} else if (nextHandler != null) {
nextHandler.handle(file);
}
}
@Override
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}
public class ImageFileHandler implements Handler {
private Handler nextHandler;
@Override
public void handle(File file) {
if (file instanceof ImageFile) {
System.out.println("Handle image file");
} else if (nextHandler != null) {
nextHandler.handle(file);
}
}
@Override
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}
public class VideoFileHandler implements Handler {
private Handler nextHandler;
@Override
public void handle(File file) {
if (file instanceof VideoFile) {
System.out.println("Handle video file");
} else if (nextHandler != null) {
nextHandler.handle(file);
}
}
@Override
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}
// 添加默認處理器處理未知文件類型
public class DefaultFileHandler implements Handler {
@Override
public void handle(File file) {
System.out.println("Cannot handle this file type: " + file.getClass().getSimpleName());
}
@Override
public void setNextHandler(Handler nextHandler) {
// 末端處理器,不需要設置下一個處理器
}
}
public class Main {
public static void main(String[] args) {
Handler textFileHandler = new TextFileHandler();
Handler imageFileHandler = new ImageFileHandler();
Handler videoFileHandler = new VideoFileHandler();
Handler defaultFileHandler = new DefaultFileHandler(); // 新增默認處理器
textFileHandler.setNextHandler(imageFileHandler);
imageFileHandler.setNextHandler(videoFileHandler);
videoFileHandler.setNextHandler(defaultFileHandler); // 設置末端處理器
TextFile textFile = new TextFile();
textFileHandler.handle(textFile);
ImageFile imageFile = new ImageFile();
textFileHandler.handle(imageFile);
VideoFile videoFile = new VideoFile();
textFileHandler.handle(videoFile);
UnknownFile unknownFile = new UnknownFile();
textFileHandler.handle(unknownFile); // 現在會輸出無法處理的信息
}
}2.12 表驅動法
一種編程模式,其核心思想是將條件邏輯與處理邏輯分離,并將條件邏輯存儲在表格(或數據結構)中,從而避免使用大量的if-else或switch-case語句,如下示例:
public String doAction1(Player input) {
//do sth 1
return "sth 1";
}
public String doAction2(Player input) {
//do sth 2
return "sth 2";
}
public String doAction3(Player input) {
//do sth 3
return "sth 3";
}
public void calc(Player input) {
Integer age = input.getAge();
if (age.equals(18)) {
doAction1(input);
} else if (age.equals(20)) {
doAction2(input);
} else if (age.equals(30)) {
doAction3(input);
}
}優化后:
public void calc(Player input) {
Map<Integer, Function> actionMappings = new HashMap<>();
actionMappings.put(18, (someParams) -> doAction1(input));
actionMappings.put(20, (someParams) -> doAction2(input));
actionMappings.put(30, (someParams) -> doAction3(input));
actionMappings.get(input.getAge()).apply(input);
}


























