程序里随处可见的interface,真的有用吗?真的用对了吗?

这两天在和一小伙伴研究解决RabbitMQ集群重启慢导致Consumer自动重连超时的问题,已经有了解决方案。接下来需要做个整理。由于同时涉及到springboot自动配置、springboot-amqp、spring-rabbit等诸多技术,先往后拖一下。

程序里随处可见的interface,真的有用吗?真的用对了吗?

本文说什么呢?通过一个程序案例来聊聊程序里随处可见的interface。

先来个四连问:什么情况下定义interface?为什么要定义interface?定义interface是为了什么?你用对interface了吗?

接下来看这个案例吧。
程序里使用了RabbitMQ,下面 MQSender 是个interface,定义了生产者往mq放消息的两种方式:

package com.yft.rabbitmq.service;

import com.yft.rabbitmq.constant.BindingEnum;

/**
 * 延迟发送服务类
 *
 * @author liuhongjie hongjie.liu@serviceshare.com
 * @date 2022年06月12日
 */
public interface MQSender {

    /**
     * 发送消息
     *
     * @param bindingEnum  声明binding的enum
     * @param msg          推送的消息
     * @param delaySeconds 延迟的时间,秒
     */
    void sendDelayMsg(BindingEnum bindingEnum, Object msg, int delaySeconds);

    /**
     * 发送消息
     *
     * @param bindingEnum 声明binding的enum
     * @param msg         推送的消息
     */
    void sendMsg(BindingEnum bindingEnum, Object msg);
}

其中 BindingEnum 是个枚举,封装定义了exchange和queue及两者的binding关系

程序里随处可见的interface,真的有用吗?真的用对了吗?程序里随处可见的interface,真的有用吗?真的用对了吗?
package com.yft.rabbitmq.constant;

public enum BindingEnum {

    SYNC_REVIEW_RECORD("sync-review-record", "sync-review-record", "sync-review-record"),
    PAY_SETTLE("pay-settle","pay-settle","pay-settle"),
    PAY_SETTLE_QUERY_DELAY("pay-settle-query-delay", "pay-settle-query-delay", "pay-settle-query-delay"),
    ;
    String exchangeName;
    String queueName;
    String routingKey;

    private static final String EXCHANGE_NAME_PREFIX = "exchange.levy-platform.";
    private static final String QUEUE_NAME_PREFIX = "queue.levy-platform.";
    private static final String ROUTING_KEY_PREFIX = "bindingKey.levy-platform.";

    BindingEnum(String exchangeName, String queueName, String routingKey) {
        this.exchangeName = exchangeName;
        this.queueName = queueName;
        this.routingKey = routingKey;
    }

    public String getExchangeName() {
        return EXCHANGE_NAME_PREFIX + exchangeName;
    }

    public String getQueueName() {
        return QUEUE_NAME_PREFIX + queueName;
    }

    public String getRoutingKey() {
        return ROUTING_KEY_PREFIX + routingKey;
    }
}

View Code

MQSender只有一个实现类 DefaultMQSender 。我们同样贴出来它的代码

程序里随处可见的interface,真的有用吗?真的用对了吗?程序里随处可见的interface,真的有用吗?真的用对了吗?
package com.yft.rabbitmq.service;

import com.yft.rabbitmq.constant.BindingEnum;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.core.AmqpTemplate;

/**
 * 延迟发送的默认实现
 *
 * @author liuhongjie hongjie.liu@serviceshare.com
 * @date 2022年06月12日
 */
@RequiredArgsConstructor
public class DefaultMQSender implements MQSender {

    private final AmqpTemplate amqpTemplate;

    @Override
    public void sendDelayMsg(BindingEnum bindingEnum, Object msg, int delaySeconds) {
        amqpTemplate.convertAndSend(bindingEnum.getExchangeName(), bindingEnum.getRoutingKey(), msg, message -> {
            message.getMessageProperties().setDelay(delaySeconds * 1000);
            return message;
        });
    }

    @Override
    public void sendMsg(BindingEnum bindingEnum, Object msg) {
        amqpTemplate.convertAndSend(bindingEnum.getExchangeName(), bindingEnum.getRoutingKey(), msg);
    }
}

View Code

使用的话,见下面的 MQSenderConfig,它定义了相关的bean

程序里随处可见的interface,真的有用吗?真的用对了吗?程序里随处可见的interface,真的有用吗?真的用对了吗?
package com.cn.yft.config;

import com.yft.rabbitmq.service.DefaultMQSender;
import com.yft.rabbitmq.service.MQSender;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Author wjx
 * @Date 2022/7/6
 */
@Configuration
public class MQSenderConfig {

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @Bean
    public MQSender mqSender() {
        return new DefaultMQSender(rabbitTemplate);
    }
}

View Code

案例介绍完毕。

程序里随处可见的interface,真的有用吗?真的用对了吗?

那么,MQSender 这个interface的作用是什么?

当事人回答:作用是我能很方便的看到这个接口的能力。
当事人回答:如果以后不用RabbitMQ,新的消息中间件直接实现这个interface就行了。

如此几次对答后,几分钟后,小伙子开始觉得这个interface好像意义并不明显。

以这个程序实现场景来看,去掉这个interface是可以的,反而还会增强程序易读性。

那么,以这个场景来说,怎么定义一个合理的interface呢?
我画了下面的草图,图样图森破。可爱的小伙立即提出了新的疑惑,我当然明白他的疑惑。秉承我的风格,我并没有继续阐开,而是让这小伙后续琢磨琢磨。

程序里随处可见的interface,真的有用吗?真的用对了吗?

程序里随处可见的interface,真的有用吗?真的用对了吗?

好,在这里,我揭晓我的想法。

MQSender 摇身一变成:

package com.yft.rabbitmq.service;

import com.yft.dto.MQMsgModel;

/**
 * 延迟发送服务类
 */
public interface MQSender {
    /**
     * 发送消息
     * @param mqMsg mq消息对象
     */
    void sendMsg(MQMsgModel mqMsg);
}

它的两个实现类:DefaultMQSender 是实时发送消息,DelayMQSender 是延迟发送消息

package com.yft.rabbitmq.service;

/**
 * 即时发送消息的实现
 */
@RequiredArgsConstructor
public class DefaultMQSender implements MQSender {

    private final AmqpTemplate amqpTemplate;

    @Override
    public void sendMsg(MQMsgModel mqMsgModel) {
        BindingEnum bindingEnum = mqMsgModel.getBindingEnum();
        amqpTemplate.convertAndSend(bindingEnum.getExchangeName(), bindingEnum.getRoutingKey(), mqMsgModel.getMsg());
    }
}
package com.yft.rabbitmq.service;

/**
 * 延迟发送消息的实现
 */
@RequiredArgsConstructor
public class DelayMQSender implements MQSender {

    private final AmqpTemplate amqpTemplate;

    @Override
    public void sendMsg(MQMsgModel mqMsgModel) {
        BindingEnum bindingEnum = mqMsgModel.getBindingEnum();
        amqpTemplate.convertAndSend(bindingEnum.getExchangeName(), bindingEnum.getRoutingKey(), mqMsgModel.getMsg(), message -> {
            message.getMessageProperties().setDelay(mqMsgModel.getDelaySeconds() * 1000);
            return message;
        });
    }
}

注意到多了一个 MQMsgModel, 好,我们来看这个 MQMsgModel,它是一个数据传输对象,定义了mq消息的属性

package com.yft.dto;

import com.yft.rabbitmq.constant.BindingEnum;
import lombok.Data;

/**
 * mq消息对象
 */
@Data
public class MQMsgModel{
    private BindingEnum bindingEnum;
    private Object msg;
    /**
     * 指定消息的延迟时间,单位:秒  →→→→(非延迟消息,不用指定)
     */
    private Integer delaySeconds;
}

使用的话, MQSenderConfig 定义两个bean就OK了

package com.cn.yft.config;

@Configuration
public class MQSenderConfig {

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @Bean
    public MQSender mqSender() {
        return new DefaultMQSender(rabbitTemplate);
    }

    @Bean
    public MQSender delayMqSender() {
        return new DelayMQSender(rabbitTemplate);
    }
}

(完毕)

程序里随处可见的interface,真的有用吗?真的用对了吗?

再贴一下上面的四连问:什么情况下定义interface?为什么要定义interface?定义interface是为了什么?你用对interface了吗?

不知你是否有了一些答案?

Original: https://www.cnblogs.com/buguge/p/16526595.html
Author: buguge
Title: 程序里随处可见的interface,真的有用吗?真的用对了吗?

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/591624/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 红黑树

    2-3-4树 JAVA技术交流群:737698533 定义 所有的叶子节点都拥有相同的深度 节点只能是2-节点,3-节点,或者4-节点 2节点 包含一个元素的节点,有两个子节点 3…

    数据库 2023年6月16日
    057
  • MySQL中 VARCHAR 可设置的最大长度是多少?

    在使用MySQL的过程中,在存储字符串时,大家或许都有过这样或那样的困惑,譬如: 对于固定长度的字符串,为什么推荐使用 CHAR 来存储? VARCHAR 可设置的最大长度是多少?…

    数据库 2023年6月11日
    086
  • Linx__Ubuntu_APT

    apt是Advanced Packaging Tool的简称。 在Ubuntu下,我们可以使用apt命令进行软件包的 更新, 安装, 删除, 清理等 类似于Windows的软件管理…

    数据库 2023年6月14日
    0110
  • MySQL索引详解,面试必问

    1、什么是索引? 索引是帮助MySQL高效获取数据的数据结构(有序)。 除了数据之外,数据库系统还维护满足特定查找算法的数据结构,这些查找算法以某种方式引用(指向)数据,从而可以在…

    数据库 2023年5月24日
    0105
  • MySQL连接的建立与使用

    在 MYSQL的启动过程中,可以看到在 mysqld_main() 函数的最后调用了 mysqld_socket_acceptor->connection_event_loo…

    数据库 2023年6月9日
    071
  • 获取不到http请求头自定义参数

    对外提供的API,需请求方在http请求头中传app_id(下划线分割) 然后服务端通过request.getHeader(“app_id”)获取不到对应的…

    数据库 2023年6月11日
    071
  • Mybatis-Plus json 格式数据查询

    // List 存储 json 格式后的查询 .apply("JSON_CONTAINS(JSON_EXTRACT(order_id_list, ‘$.data[*]’)…

    数据库 2023年6月6日
    0259
  • haproxy服务部署

    haproxy haproxy 一、haproyx是什么 二、负载均衡类型 三、部署haproxy 1.源码部署haproxy 2.Haproxy搭建http负载均衡 一、hapr…

    数据库 2023年6月14日
    098
  • Mysql Date操作

    根据format字符串格式化date值。 下列修饰符可以被用在format字符串中: %W 星期名字(Sunday……Saturday) %D 有英语前缀的月份的日期(1s…

    数据库 2023年6月14日
    072
  • 巧用自定义注解,一行代码搞定审计日志

    任何一个软件系统,都不可避免的会碰到【 信息安全】这个词,尤其是对于刚入行的新手,比如我,我刚入行的时候,领导让我做一个数据报表导出功能,我就按照他的意思去做,至于谁有权限操作导出…

    数据库 2023年6月14日
    058
  • Java百度地图经纬度纠偏

    在国内使用电子地图获取到的经纬度都不是真实的经纬度,而是经过一定的算法在真实的经纬度上添加了一个偏移量,且不同的地图有不同的算法。现在告诉大家在java中怎样对百度地图进行纠偏,主…

    数据库 2023年6月9日
    073
  • 普通 Docker 与 Kubernetes 对比

    Docker提供基本容器管理 API 和容器镜像文件格式Kubernetes 管理运行容器的(物理或虚拟)主机群集,如果 Docker 是 OCP 的”内核&#8221…

    数据库 2023年6月14日
    064
  • 源码 | 为金融场景而生的数据类型:Numeric

    高日耀 资深数据库内核研发毕业于华中科技大学,喜欢研究主流数据库架构和源码,并长期从事分布式数据库内核研发。曾参与分布式 MPP 数据库 CirroData 内核开发(东方国信),…

    数据库 2023年5月24日
    085
  • 安装node、npm、vue cli脚手架

    1、node https://www.runoob.com/nodejs/nodejs-install-setup.html 2、npm 安装好node就默认安装好npm 不需要单…

    数据库 2023年6月9日
    073
  • 3_肯德基餐厅信息查询_动态加载_post请求

    肯德基餐厅信息查询网址:http://www.kfc.com.cn/kfccda/storelist/index.aspx import requests url = ‘http:…

    数据库 2023年6月11日
    069
  • MySQL学习(3)—MySQL常用命令

    ps:此随笔基于mysql 5.7.*版本。 准备 net start mysql 启动MySQL服务 net stop mysql 关闭MySQL服务 mysql [-h exi…

    数据库 2023年6月14日
    064
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球