0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

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

京东云 来源:jf_75140285 作者:jf_75140285 2024-11-21 14:06 次阅读

作者:京东物流 杨唯一

一、Java 枚举类

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

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

二、枚举+策略模式

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

1、策略模式

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

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

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

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

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

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

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

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

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

代码如下:

class OperationAdd implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) {
         return num1 + num2; 
    } 
}
class OperationSub implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 - num2;
    } 
}

class OperationMul implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 * num2; 
    } 
}

class OperationDiv implements CalculateStrategy { 
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 / num2; 
    } 
}

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

class CalculatorContext { 
    private CalculateStrategy strategy; 
   
    public CalculatorContext(CalculateStrategy strategy) {
        this.strategy = strategy; 
    } 
    public int executeStrategy(int num1, int num2) { 
        return strategy.doOperation(num1, num2); 
    }
}

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

public static void main(String[] args) {
    int a=4,b=2;

    CalculatorContext context = new CalculatorContext(new OperationAdd());
    System.out.println("a + b = "+context.executeStrategy(a, b));

    CalculatorContext context2 = new CalculatorContext(new OperationSub());
    System.out.println("a - b = "+context2.executeStrategy(a, b));

    CalculatorContext context3 = new CalculatorContext(new OperationMul());
    System.out.println("a * b = "+context3.executeStrategy(a, b));

    CalculatorContext context4 = new CalculatorContext(new OperationDiv());
    System.out.println("a / b = "+context4.executeStrategy(a, b)); }

输出结果:

 a + b = 6
 a - b = 2 
 a * b = 8 
 a / b = 2

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

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

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

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

@Getter
public enum CalculateStrategyEnum {    
    ADD("operationAdd"),    
    SUB("operationSub"),    
    MUL("operationMul"),
    DIV("operationDiv");
    private final String strategy;    
    CalculateStrategyEnum(String strategy) {
        this.strategy = strategy;    
    }    
    public static CalculateStrategyEnum getStrategyEnum(String operationName) {        
        switch (operationName) {            
            case "加法运算":                
                return ADD;            
            case "减法运算":                
                return SUB;            
            case "乘法运算":                
                return MUL;            
            case "除法运算":                
                return DIV;            
            default:                
                return null;        
        }    
    }
}

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

@Component
class OperationAdd implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) {
         return num1 + num2; 
    } 
}

@Component
class OperationSub implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 - num2;
    } 
}

@Component
class OperationMul implements CalculateStrategy {
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 * num2; 
    } 
}

@Component
class OperationDiv implements CalculateStrategy { 
    @Override 
    public int doOperation(int num1, int num2) { 
        return num1 / num2; 
    } 
}

策略调用处:业务处理

@Service
class CalculateService {
    // 注入所有策略类
    @Resource
    Map< String, CalculateStrategy > calculateStrategy;
   
    public executeStrategy(String operationName, int num1, int num2) {   
        // 根据参数匹配对应的枚举实例
        CalculateStrategyEnum strategy = CalculateStrategyEnum.getStrategyEnum(operationName);
        
        // 获取对应bean执行策略算法
        if (stategy != null) {
            calculateStrategy.get(strategy.getStrategy()).doOperation(num1, num2)
        }      
    } 
}

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

三、枚举+函数式接口

1、函数式接口

什么是函数式接口

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

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

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

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

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

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

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

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

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

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

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

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

public enum ActionEnum {    
    RUN(ActionEnum::run),    
    JUMP(ActionEnum::jump),    
    SIT(ActionEnum::sit);    
    
    private final Runnable action;   
     
    ActionEnum(Runnable action) {        
        this.action = action;    
    }    
    
    public static void run() {        
        System.out.println("Running");    
    }    
    
    public static void jump() {        
        System.out.println("Jumping");    
    }    
    
    public static void sit() {        
        System.out.println("Sitting");    
    }
}

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

public enum ActionEnum {    
    RUN(ActionEnum::run),    
    JUMP(ActionEnum::jump),    
    SIT(ActionEnum::sit);    
    
    private final Supplier< String > action;    
    
    ActionEnum(Supplier< String > action) {        
        this.action = action;    
    } 
       
    public static String run() {        
        return "Running";    
    }    
    
    public static String jump() {        
        return "Jumping";    
    }    
    
    public static String sit() {        
        return "Sitting";    
    }
}

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

引用:

策略模式介绍:

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

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    8486

    浏览量

    150803
  • JAVA
    +关注

    关注

    19

    文章

    2956

    浏览量

    104530
收藏 人收藏

    评论

    相关推荐

    使用C语言实现耦合

    编程时,我们讲究的是耦合,在协同开发、代码移植、维护等环节都起到很重要的作用。
    发表于 09-21 11:33 1157次阅读

    如何实现代码的耦合

    大家在谈到面向对象编程的时候基本都是讨论软件的“耦合”的特点,这6个字也是算是成为了大部分评判代码质量的一个标准,所以很多小伙伴一
    发表于 10-13 08:57 1021次阅读

    如何去实现模块设计中的耦合

    耦合分别是什么意思?有何作用?如何去实现模块设计中的
    发表于 02-25 07:05

    怎么把硬件操作和APP软件耦合起来?

    你们写嵌入都是怎么把硬件操作和APP软件耦合起来的,怎样达到
    发表于 11-06 07:57

    深入理解java枚举类型enum用法

    的子类(java.lang.Enum 是一个抽象类)。枚举类型符合通用模式 Class Enum packagecom.hmw.test; /** * 枚举测试类 *@author《
    发表于 09-27 11:49 0次下载

    C语言中的耦合讲解

    编程时,我们讲究的是耦合,在协同开发、代码移植、维护等环节都起到很重要的作用。 一、原理篇而
    的头像 发表于 08-16 14:06 2303次阅读

    C语言--“耦合”编程思想

    1、定义耦合,是软件工程中的概念,是判断设计好坏的标准,主要是面向对象的设计,主要是看类的内聚性是否
    发表于 01-13 13:43 3次下载
    C语言--“<b class='flag-5'>高</b><b class='flag-5'>内</b><b class='flag-5'>聚</b>,<b class='flag-5'>低</b><b class='flag-5'>耦合</b>”编程思想

    很强大!耦合的MCU实用软件框架

    一个模块内部各个元素之间的联系的紧密程度,如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,也就是
    发表于 02-08 15:56 3次下载
    很强大!<b class='flag-5'>低</b><b class='flag-5'>耦合</b><b class='flag-5'>高</b><b class='flag-5'>内</b><b class='flag-5'>聚</b>的MCU实用软件框架

    为什么我不再推荐枚举策略模式

    我们可以看到经典方法,创建了一个接口、三个策略类,还是比较啰嗦的。调用类的实现也待商榷,新增一个策略类还要修改榜单实例(可以用抽象工厂解决,但是复杂度又上升了)。加之我们有更好的选择,
    的头像 发表于 04-14 10:52 1996次阅读

    Java枚举的特点及用法

    Java 枚举出现之前,通常会使用常量类来表示一组固定的常量值,直到Java 1.5之后推出了枚举,那么枚举类型有哪些特点,它比常量类又
    的头像 发表于 09-30 10:02 1361次阅读

    函数接口的应用知识点

    )对java.util.function包进行解读 (原理篇)介绍函数接口实现原理 应用篇将阶段相关的JDK源码以及给出典型的示例代码
    的头像 发表于 10-13 11:32 614次阅读
    <b class='flag-5'>函数</b><b class='flag-5'>式</b><b class='flag-5'>接口</b>的应用知识点

    什么是函数接口

    、及其所使用的一些和函数接口相关的知识点进行一个全面的学习。函数接口所涉及的知识点包含:
    的头像 发表于 10-13 14:48 1467次阅读
    什么是<b class='flag-5'>函数</b><b class='flag-5'>式</b><b class='flag-5'>接口</b>

    java switch case值能为枚举值吗

    Java中的switch语句可以接受枚举类型的值作为参数。在Java中,枚举是一种特殊的数据类型,它定义了一个固定数量的命名常量。因此,可以将枚举
    的头像 发表于 11-30 14:41 5048次阅读

    Java中保持扩展性的实现方法

    SOLID(单一、开闭、里替换、接口隔离、依赖倒置)五大原则和23种设计模式(常见的单例、构建者、装饰、适配、代理、组合、模板等等),小伙伴们对这些肯定都很熟悉。这些原则和设计模式
    的头像 发表于 12-01 10:01 345次阅读
    <b class='flag-5'>Java</b>中保持扩展性的<b class='flag-5'>实现</b>方法

    编程如何做到耦合呢?

    耦合,是指模块之间尽可能的使其独立存在,模块之间不产生联系不可能,但模块与模块之间的接口应该尽量少而简单。
    的头像 发表于 12-06 09:20 958次阅读