一、代码中常见if-else弊端
if-else 是一种常见的流程控制语句,用于根据条件执行不同的操作。然而,在代码中过多地使用 if-else 分支结构会带来一些弊端,具体包括:
1. 可读性差: 过多的 if-else 分支结构会使代码变得复杂和难以阅读,尤其是嵌套的 if-else 语句,容易引起“深度坍塌”问题。
2. 维护成本高: 当需求发生变化时,需要修改 if-else 分支结构对应的代码分支,如果分支较多,则修改成本相应就会增加。
3. 可扩展性差: 如果需要添加新的分支,就需要在 if-else 分支结构中添加新的代码分支,可能会影响原有代码的逻辑和稳定性。
4. 难以测试: 由于 if-else 分支结构存在多个代码分支,因此很难对所有情况进行全面、有效的测试,可能会出现一些漏洞或错误。
例如一下代码:
public String getGrade(int score) {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else if (score >= 60) {
return "D";
} else if
...
}
二、策略模式+工厂模式消除
策略模式和工厂模式可以结合起来使用,以消除 if-else 分支结构。具体步骤如下:
-
定义一个策略接口,该接口包含所需的方法。
-
定义多个实现策略接口的类,每个类实现策略接口中定义的方法,代表着不同的逻辑分支。
-
定义一个工厂类,用于根据输入参数返回对应的策略对象。
-
在客户端代码中,通过工厂类获取所需的策略对象,并调用其方法完成业务操作。
下面是一个示例代码,该代码使用策略模式和工厂模式消除了 if-else 分支结构:
// 定义策略接口
public interface GradeStrategy {
String getGrade(int score);
}
// 定义多个实现策略接口的类
public class AGradeStrategy implements GradeStrategy {
@Override
public String getGrade(int score) {
return "A";
}
}
public class BGradeStrategy implements GradeStrategy {
@Override
public String getGrade(int score) {
return "B";
}
}
public class CGradeStrategy implements GradeStrategy {
@Override
public String getGrade(int score) {
return "C";
}
}
public class DGradeStrategy implements GradeStrategy {
@Override
public String getGrade(int score) {
return "D";
}
}
public class FGradeStrategy implements GradeStrategy {
@Override
public String getGrade(int score) {
return "F";
}
}
// 定义工厂类
public class GradeStrategyFactory {
private static final RangeMap<Integer, GradeStrategy> RANGE_MAP = TreeRangeMap.create();
static {
RANGE_MAP.put(Range.closed(90, 100), new AGradeStrategy());
RANGE_MAP.put(Range.closed(80, 89), new BGradeStrategy());
RANGE_MAP.put(Range.closed(70, 79), new CGradeStrategy());
RANGE_MAP.put(Range.closed(60, 69), new DGradeStrategy());
RANGE_MAP.put(Range.closed(0, 59), new FGradeStrategy());
}
public static GradeStrategy getGradeStrategy(int score) {
return RANGE_MAP.get(score);
}
}
// 在客户端代码中,通过工厂类获取策略对象,并调用其方法完成业务操作
public String getGrade(int score) {
GradeStrategy gradeStrategy = GradeStrategyFactory.getGradeStrategy(score);
return gradeStrategy.getGrade(score);
}
在上述代码中,我们首先定义了一个 GradeStrategy 接口,然后定义了五个实现该接口的类,分别代表不同的逻辑分支。接着,我们定义了一个 GradeStrategyFactory 工厂类,用于根据输入参数返回对应的策略对象。在客户端代码中,我们通过工厂类获取所需的策略对象,并调用其方法完成业务操作,从而消除了 if-else 分支结构。
使用策略模式和工厂模式结合起来可以使代码更加灵活、易扩展和可维护,避免了 if-else 分支结构带来的问题。
我们再看一下上述代码UML类图:
三、枚举方法消除
使用枚举来实现接口并消除 if-else 分支结构也是一种很好的方式。以下是使用枚举来实现相同功能的示例代码:
// 定义接口
public interface GradeStrategy {
String getGrade(int score);
}
// 枚举
public enum GradeEnum implements GradeStrategy {
A(Range.atLeast(90)) {
@Override
public String getGrade(int score) {
return "A";
}
},
B(Range.closedOpen(80, 90)) {
@Override
public String getGrade(int score) {
return "B";
}
},
C(Range.closedOpen(70, 80)) {
@Override
public String getGrade(int score) {
return "C";
}
},
D(Range.closedOpen(60, 70)) {
@Override
public String getGrade(int score) {
return "D";
}
},
F(Range.lessThan(60)) {
@Override
public String getGrade(int score) {
return "F";
}
};
private final Range<Integer> range;
GradeEnum(Range<Integer> range) {
this.range = range;
}
public Range<Integer> getRange() {
return range;
}
/**
* 等级获取
*
* @param score
* @return
*/
public static String grade(Integer score) {
return Arrays.stream(GradeEnum.values()).filter(e->e.range.contains(score)).findFirst().get().getGrade(score);
}
}
这样,我们通过简单的枚举类型定义就可以达到消除 if-else 分支结构的目的,并且代码非常简洁可读。这种方式在处理只有少量分支逻辑的情况下,尤其适合使用。需要注意的是,由于枚举类型是不可变的,因此不存在线程安全问题。
四、Spring服务定位消除
1. 描述
服务定位模式(Service Locator Pattern)是一种经典的设计模式,用于实现组件之间的解耦和松散耦合。该模式将服务的查找和创建过程抽象出来,封装到一个服务定位器中,客户端可以通过服务定位器获取所需的服务实例,而不需要直接依赖于特定的服务实现类。
2. UML类图
我们采用不同数据源的案例来阐述说明
3. 示例代码
数据源接口
public interface DataSource {
/**
* 执行更新操作
* @param sql
*/
void update(String sql);
}
数据源接口实现类
@Component("MYSQL")// 这里要和枚举保持一致
public class MysqlDataSource implements DataSource {
@Override
public void update(String sql) {
System.out.println("mysql数据库执行更新操作");
}
}
@Component("ORACLE")// 这里要和枚举保持一致
public class OracleDataSource implements DataSource {
@Override
public void update(String sql) {
System.out.println("oracle执行更新操作");
}
}
@Component("SQLSERVER")// 这里要和枚举保持一致
public class SqlServerDataSource implements DataSource {
@Override
public void update(String sql) {
System.out.println("sqlserver执行更新操作");
}
}
数据源配置类
@Configuration
public class DataSourceConfig {
@Bean("dataSourceFactory")
public FactoryBean serviceLocatorFactoryBean() {
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
// 设置服务定位接口
factoryBean.setServiceLocatorInterface(DataSourceFactory.class);
return factoryBean;
}
}
public interface DataSourceFactory {
/**
* 获取数据源
* @param type
* @return
*/
DataSource getDataSource(DataSourceEnum type);
}
数据源枚举类
public enum DataSourceEnum {
MYSQL,
ORACLE,
SQLSERVER;
}
客户端
@Component
public class SLFBClient {
@Autowired
private DataSourceFactory dataSourceFactory;
public void update(DataSourceEnum type) {
dataSourceFactory.getDataSource(type).update("update table set...");
}
}
测试
@SpringBootTest(classes = SpringBootDesignerApplication.class)
public class AppTest {
@Resource
private SLFBClient slfbClient;
@Test
public void sLFBClientTest() {
slfbClient.update(DataSourceEnum.MYSQL);
}
}
五、 函数式编程消除
函数式编程可以通过 Lambda 表达式和函数式接口来消除 if-else 分支结构,使代码更加简洁、易读和可维护。下面是一个使用函数式编程消除 if-else 分支结构的示例代码:
// 创建函数式接口
public interface GradeFunction {
String apply(int score);
}
// 客户端测试代码
public class GradeFunctionTest {
public static String getGrade(int score) {
RangeMap<Integer, GradeFunction> gradeFunctions = TreeRangeMap.create();
gradeFunctions.put(Range.atLeast(90), score1 -> "A");
gradeFunctions.put(Range.closedOpen(80, 90), score1 -> "B");
gradeFunctions.put(Range.closedOpen(70, 80), score1 -> "C");
gradeFunctions.put(Range.closedOpen(60, 70), score1 -> "D");
gradeFunctions.put(Range.lessThan(60), score1 -> "F");
GradeFunction gradeFunction = gradeFunctions.get(score);
return gradeFunction != null ? gradeFunction.apply(score) : "";
}
public static void main(String[] args) {
System.out.println(getGrade(30));
}
}
是不是感觉代码很精简,甚至可以更加简洁,直接去掉自定义函数式接口,具体代码如下:
public class GradeFunctionTest {
public static String getGrade(int score) {
RangeMap<Integer, Function<Integer, String>> gradeFunctions = TreeRangeMap.create();
gradeFunctions.put(Range.atLeast(90), score1 -> "A");
gradeFunctions.put(Range.closedOpen(80, 90), score1 -> "B");
gradeFunctions.put(Range.closedOpen(70, 80), score1 -> "C");
gradeFunctions.put(Range.closedOpen(60, 70), score1 -> "D");
gradeFunctions.put(Range.lessThan(60), score1 -> "F");
final Function<Integer, String> integerStringFunction = gradeFunctions.get(score);
return integerStringFunction != null ? integerStringFunction.apply(score) : "";
}
public static void main(String[] args) {
System.out.println(getGrade(30));
}
}