Java 枚举与策略模式、函数式接口的结合:实现高内聚低耦合的设计

作者:京东物流 杨唯一

一、Java 枚举类

Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一年的 12 个月份,一个星期的 7 天,方向有东南西北等。

我们在业务需求开发中,通常会使用枚举来定义业务上的一组常量,那除了简单地定义常量之外,我们如何利用枚举来实现高内聚、低耦合的设计呢?下面介绍下枚举和策略模式、函数式接口的组合应用。

二、枚举+策略模式

首先介绍下策略模式,已经掌握策略模式的同学可以直接跳过此部分。

1、策略模式

策略模式(Strategy Pattern)属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。 其主要目的是通过定义相似的算法,替换if else 语句写法,并且可以随时相互替换。

策略模式主要由这三个角色组成,环境角色(Context)、抽象策略角色(Strategy)和具体策略角色(ConcreteStrategy)。

•环境角色(Context):持有一个策略类的引用,提供给客户端使用。

•抽象策略角色(Strategy):这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。

•具体策略角色(ConcreteStrategy):包装了相关的算法或行为。

在这里插入图片描述



这里为了方便理解,我们就拿刚学习Java的时候使用计算方法来说吧。 在使用计算器进行计算的时候,会经常用到加减乘除方法。如果我们想得到两个数字相加的和,我们需要用到“+”符号,得到相减的差,需要用到“-”符号等等。虽然我们可以通过字符串比较使用if/else写成通用方法,但是计算的符号每次增加,我们就不得不加在原先的方法中进行增加相应的代码,如果后续计算方法增加、修改或删除,那么会使后续的维护变得困难。 但是在这些方法中,我们发现其基本方法是固定的,这时我们就可以通过策略模式来进行开发,可以有效避免通过if/else来进行判断,即使后续增加其他的计算规则也可灵活进行调整。

首先定义一个抽象策略角色,并拥有一个计算的方法。

1interface CalculateStrategy { 2 int doOperation(int num1, int num2); 3}

然后再定义加减乘除这些具体策略角色并实现方法。

代码如下:

1class OperationAdd implements CalculateStrategy { 2 @Override 3 public int doOperation(int num1, int num2) { 4 return num1 + num2; 5 } 6} 7class OperationSub implements CalculateStrategy { 8 @Override 9 public int doOperation(int num1, int num2) { 10 return num1 - num2; 11 } 12} 13 14class OperationMul implements CalculateStrategy { 15 @Override 16 public int doOperation(int num1, int num2) { 17 return num1 * num2; 18 } 19} 20 21class OperationDiv implements CalculateStrategy { 22 @Override 23 public int doOperation(int num1, int num2) { 24 return num1 / num2; 25 } 26}

最后在定义一个环境角色,提供一个计算的接口供客户端使用。 代码如下:

1class CalculatorContext { 2 private CalculateStrategy strategy; 3 4 public CalculatorContext(CalculateStrategy strategy) { 5 this.strategy = strategy; 6 } 7 public int executeStrategy(int num1, int num2) { 8 return strategy.doOperation(num1, num2); 9 } 10}

编写好之后,那么我们来进行测试。 测试代码如下:

1public static void main(String[] args) { 2 int a=4,b=2; 3 4 CalculatorContext context = new CalculatorContext(new OperationAdd()); 5 System.out.println("a + b = "+context.executeStrategy(a, b)); 6 7 CalculatorContext context2 = new CalculatorContext(new OperationSub()); 8 System.out.println("a - b = "+context2.executeStrategy(a, b)); 9 10 CalculatorContext context3 = new CalculatorContext(new OperationMul()); 11 System.out.println("a * b = "+context3.executeStrategy(a, b)); 12 13 CalculatorContext context4 = new CalculatorContext(new OperationDiv()); 14 System.out.println("a / b = "+context4.executeStrategy(a, b)); }

输出结果:

1 a + b = 6 2 a - b = 2 3 a * b = 8 4 a / b = 2

上面这段是网上常见的对于策略模式的介绍和示例,但在这段测试代码中,我们对于策略的选择依然是在具体的调用处通过对CalculatorContext类构造器的传参去指定的,那么如何能动态地选择策略并将选择策略的具体逻辑抽取,就要用到枚举+策略这套组合拳啦。

2、枚举+策略模式的使用

定义策略模式的枚举类,并将具体策略的 bean 名称作为枚举类 strategy 的值;

在getStrategyEnum方法中,我们可以去实现选择策略的逻辑,将选择策略的判断逻辑内聚在枚举类中,与业务代码隔离,当然在具体业务中,我们对于策略选择的判断会更复杂,此处只是举个简单的例子说明下:

1@Getter 2public enum CalculateStrategyEnum { 3 ADD("operationAdd"), 4 SUB("operationSub"), 5 MUL("operationMul"), 6 DIV("operationDiv"); 7 private final String strategy; 8 CalculateStrategyEnum(String strategy) { 9 this.strategy = strategy; 10 } 11 public static CalculateStrategyEnum getStrategyEnum(String operationName) { 12 switch (operationName) { 13 case "加法运算": 14 return ADD; 15 case "减法运算": 16 return SUB; 17 case "乘法运算": 18 return MUL; 19 case "除法运算": 20 return DIV; 21 default: 22 return null; 23 } 24 } 25}

策略实现类中加入@Component 注解,将 bean 实例交给 spring 容器管理

1@Component 2class OperationAdd implements CalculateStrategy { 3 @Override 4 public int doOperation(int num1, int num2) { 5 return num1 + num2; 6 } 7} 8 9@Component 10class OperationSub implements CalculateStrategy { 11 @Override 12 public int doOperation(int num1, int num2) { 13 return num1 - num2; 14 } 15} 16 17@Component 18class OperationMul implements CalculateStrategy { 19 @Override 20 public int doOperation(int num1, int num2) { 21 return num1 * num2; 22 } 23} 24 25@Component 26class OperationDiv implements CalculateStrategy { 27 @Override 28 public int doOperation(int num1, int num2) { 29 return num1 / num2; 30 } 31}

策略调用处:业务处理

1@Service 2class CalculateService { 3 // 注入所有策略类 4 @Resource 5 Map<String, CalculateStrategy> calculateStrategy; 6 7 public executeStrategy(String operationName, int num1, int num2) { 8 // 根据参数匹配对应的枚举实例 9 CalculateStrategyEnum strategy = CalculateStrategyEnum.getStrategyEnum(operationName); 10 11 // 获取对应bean执行策略算法 12 if (stategy != null) { 13 calculateStrategy.get(strategy.getStrategy()).doOperation(num1, num2) 14 } 15 } 16}

当我们需要新增算法时,只需新增新的策略实现类和枚举实例,修改枚举类中的策略选择方法,无需入侵现有业务代码,实现了高内聚、低耦合,增强了系统的灵活性和可扩展性。

三、枚举+函数式接口

1、函数式接口

什么是函数式接口

函数式接口是只包含一个抽象方法的接口。但是默认方法和静态方法在此接口中可以定义多个。Java 中的函数式接口可以被用作 Lambda 表达式的目标类型。通过函数式接口,可以实现更简洁、更具可读性的代码,从而支持函数式编程的思想。

Java 中有一些内置的函数式接口,用于不同的用途:

1.Runnable: 用于描述可以在单独线程中执行的任务。

2.Callable: 类似于 Runnable,但可以返回执行结果或抛出异常。

3.Comparator: 用于比较两个对象的顺序。

4.Function: 接受一个参数并产生一个结果。

5.Predicate: 接受一个参数并返回一个布尔值,用于判断条件是否满足。

6.Supplier: 不接受参数,但返回一个值。

7.Consumer: 接受一个参数,无返回值。

2、枚举+函数式接口的使用

我们可以通过为枚举类设置函数式接口类型的属性,让枚举实例拥有一些特定的行为

例如下面的例子,通过Runnable接口为枚举实例赋予了对应的行为。

1public enum ActionEnum { 2 RUN(ActionEnum::run), 3 JUMP(ActionEnum::jump), 4 SIT(ActionEnum::sit); 5 6 private final Runnable action; 7 8 ActionEnum(Runnable action) { 9 this.action = action; 10 } 11 12 public static void run() { 13 System.out.println("Running"); 14 } 15 16 public static void jump() { 17 System.out.println("Jumping"); 18 } 19 20 public static void sit() { 21 System.out.println("Sitting"); 22 } 23}

也可以根据具体行为是否消费参数,以及是否提供返回值来选择合适的接口

1public enum ActionEnum { 2 RUN(ActionEnum::run), 3 JUMP(ActionEnum::jump), 4 SIT(ActionEnum::sit); 5 6 private final Supplier<String> action; 7 8 ActionEnum(Supplier<String> action) { 9 this.action = action; 10 } 11 12 public static String run() { 13 return "Running"; 14 } 15 16 public static String jump() { 17 return "Jumping"; 18 } 19 20 public static String sit() { 21 return "Sitting"; 22 } 23}

这样,我们可以将与枚举值强关联的一些行为封装到枚举类中,与其他业务代码隔离,实现高内聚、低耦合的设计。



引用:

策略模式介绍:

https://www.cnblogs.com/xuwujing/p/9954263.html

点赞
收藏

评论区

加载中...

相关推荐

java枚举类型enum的使用

java枚举类型enum的使用最近跟同事讨论问题的时候,突然同事提到我们为什么java中定义的常量值不采用enmu枚举类型,而采用publicfinalstatic 类型来定义呢?以前我们都是采用这种方式定义的,很少采用enum定义,所以也都没有注意过,面对突入起来的问题,还真有点不太清楚为什么有这样的定义。既然不明白就抽

java枚举类型

java枚举类型enum枚举类型是java5新增特性的一部分,是一种特殊的数据类型(既是一种类类型但是又比类类型多些特殊的约束)枚举定义的方式:publicenumDay{MONDAY,TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY,SUNDAY}定义

java面试 (六)

1 String.split(Stringregex),传入的参数是正则表达式,有一些特殊字符(比如.\\()\\|等)需要转义。2 关于枚举类型,一般用作常量,理想情况下,枚举中的属性字段是私有的,并在私有构造函数中赋值,没有对应的Setter方法,最好加上final修饰符。publicenumPer

Java 实用类

实用类枚举MathRandomStringStringBuffer日期类枚举枚举(Enum)是一种有确定取值区间的数据类型,它本质上是一种类,具有简洁、安全、方便等特点。可以这样理解,枚举的值被约束到一个特定的范围,只能取这个范围以内的值。我们为什么要用枚举呢?我们在描述对象的一些属性特征时,可选择的值是一个特定范围的,不能随便定义。比如性别只

Java枚举的小知识点

enum是jdk1.5引入的,使用它可以创建枚举类型,就像使用class创建类一样。enum关键字创建的枚举类型默认是java.lang.Enum(一个抽象类)的子类用法1常量一般定义常量都是publicstaticfinal…,现在可以把相关常量都放在一个枚举类里,而且枚举比常量提供更多方法1.enumsea

Java枚举类使用

用法一:常量在JDK1.5 之前,我们定义常量都是: publicstaticfianl.... 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。 publicenumColor{RED,GREEN,BLANK,YELLOW}用法二