首页 > 编程语言 >Java 接口的所有子类都需要执行相同处理逻辑的推荐姿势

Java 接口的所有子类都需要执行相同处理逻辑的推荐姿势

时间:2023-03-21 14:36:01浏览次数:54  
标签:逻辑 Java 子类 buildPart param ResultDTO 接口 return public


一、背景

在实际开发过程中,有些时候我们可能会遇到这样的场景:我们定义接口给上游使用,不同的业务类型定义不同的子类型,实现该接口的某个函数,但是这些子类型会有很多公共的逻辑(公共的步骤)。

如果将这部分代码定义为工具方法,就需要在每个子类中都执行对应的调用。

如果有些公共步骤的返回值和接口中定义的返回值一致时,很容易出现漏调用的情况。

那么,该如何 “强制”子类型都要执行一些相同的步骤呢?

Java 接口的所有子类都需要执行相同处理逻辑的推荐姿势_java

二、描述

下面都是伪代码,大家不必纠结于具体细节,理解意思即可。

我们需要提供给上游这样一个接口, type 是指当前服务能够处理的类型,something 代表实际执行的业务功能。

public interface SomeInterface {

String type();

ResultDTO something(Param param);
}

第一个实现类:

public class AImpl implements SomeInterface {

@Override
public String type() {
return "A";
}

@Override
public ResultDTO something(Param param) {
// 特有逻辑
ResultDTO resultDTO = buildPart(param);

// 构造公共逻辑所需的参数
OtherParam otherParam = new OtherParam();

// 公有逻辑
return SomeUtils.fillCommon(resultDTO, otherParam);
}

private ResultDTO buildPart(Param param) {
ResultDTO result = new ResultDTO();
// 执行查询

// 塞入特有属性

return result;
}
}

第二个实现类:

public class BImpl implements SomeInterface {

@Override
public String type() {
return "B";
}

@Override
public ResultDTO something(Param param) {
// 特有逻辑
ResultDTO resultDTO = buildPart(param);

// 构造公共逻辑所需的参数
OtherParam otherParam = new OtherParam();

// 公有逻辑
return SomeUtils.fillCommon(resultDTO, otherParam);
}

private ResultDTO buildPart(Param param) {
ResultDTO result = new ResultDTO();
// 执行查询

// 塞入特有属性

return result;
}

}

使用时,构造 ​​Map<String,SomeInterface> type2BeanMap​​ ,然后根据当前的 type 去执行具体的实现 Bean。

具体可参考​​《巧用 Spring 自动注入实现策略模式升级版》​​

问题:如果我们新增 ​​CImpl​​​ 继承 ​​SomeInterface​​​ 就必须查看 ​​AImpl​​​ 或者 ​​BImpl​​ 源码才知道有一段公共逻辑,很容易遗漏这一段公共逻辑。

如果我们想让新建子类时,不需要担心遗漏这段公共的逻辑,该怎么办?

三、方案

如果大家对设计模式比较熟悉的话,这种场景我们很自然地会联想到​​模板模式​​。

我们将采用这种设计模式,对代码进行改造。

(1)我们将特有逻辑上提到接口中,在 default 方法中编排逻辑即可。

public interface SomeInterface {

String type();

// 目标方法
default ResultDTO something(Param param) {
return SomeUtils.fillCommon(buildPart(param));
}

/**
* 特有逻辑
*/
MiddleParam buildPart(Param param);
}

定义为接口的好处是,不会影响到子类继承其他父类型(Java 是单继承机制)。

(2)可以将 ​​SomeInterface​​​ 改为抽象类(​​AbstractSomeService​​​),​​something​​​定义为 ​​public​​​ ,将 ​​builPart​​ 定义为抽象函数,让子类去重写。

public abstract class AbstractSomeService {

abstract String type();

// 目标方法
public ResultDTO something(Param param) {
return SomeUtils.fillCommon(buildPart(param));
}

/**
* 特有逻辑
*/
abstract MiddleParam buildPart(Param param);
}

定义为抽象类的坏处是子类型无法再继承其他类,但理论上也不应该(不需要) 再继承其他类,好处是可以将​​buildPart​​​ 重写的访问修饰符范围降低,如改为 ​​protected​​。

注意:
(1) 本案例里的 ​​​SomeUtils.fillCommon​​​ 只是伪代码,公共逻辑可能封装在工具类中,也可能封装在某个 bean 中,在抽象类或者接口中可以通过​​ApplicationContextHolder​​​ 去获取并调用。
(2) 实际编码时,公共逻辑也未必在最后调用。
(3) 实际编码中,公共的步骤可能不止一个,但是方案是一致的,有几个定义几个抽象方法即可
(4)实际编码时,公共的步骤可能不需要返回值

定义中间参数:

@Data
public class MiddleParam {

private ResultDTO semiResult;

private OtherParam otherParam;
}

第一个实现类:

public class AImpl extends AbstractSomeService {
@Override
public String type() {
return "A";
}


@Override
protected MiddleParam buildPart(Param param) {
MiddleParam middleParam = new MiddleParam();
// 执行查询

// 塞入特有属性

return middleParam;
}
}

第二个实现类:

public class BImpl extends AbstractSomeService {

@Override
public String type() {
return "B";
}

@Override
protected MiddleParam buildPart(Param param) {
MiddleParam middleParam = new MiddleParam();
// 执行查询

// 塞入特有属性

return middleParam;
}

}

这样通过类似 ​​buildPart​​ 这种函数名,可以明确感知到当前是对部分逻辑进行处理,且不需要在当前子类中执行公共逻辑的调用。

四、总结

本文案例比较简单,主要思想是使用模板模式来实现公共步骤的编排。

希望大家遇到类似场景时,可以使用更优雅的方式,更健壮的方式去实现,而不是依靠“口口相传” 或者让别人看你代码才知道该怎么写。


创作不易,如果本文对你有帮助,欢迎点赞、收藏加关注,你的支持和鼓励,是我创作的最大动力。

Java 接口的所有子类都需要执行相同处理逻辑的推荐姿势_策略模式_02


标签:逻辑,Java,子类,buildPart,param,ResultDTO,接口,return,public
From: https://blog.51cto.com/mingmingruyue/6128827

相关文章

  • Java实现记录对象修改前后的变化
    需求描述今天接到一个需求,修改数据时需要记录修改详情。详情包括,被修改的字段,修改前的值和修改后的值。解决思路分别比较修改前后两个Bean实例的所有成员变量,当值不一致时......
  • Solon2 接口开发: 实战 Gateway 模式效果
    1、效果预览网关@Mapping("/api/v3/app/**")@ComponentpublicclassApiGateway3xextendsUapiGateway{@Overrideprotectedvoidregister(){fi......
  • Java基本类型和包装类什么情况下判断相等(“==“或“equals“)?
    Java中判断两个变量是否相等有两种方式:一种是利用==运算符,另一种是利用equals方法。如果变量是基本类型,那么使用==和使用equals会得到相同的结果,都是判断变量的值是否......
  • 使用token机制实现接口幂等性校验
    大家好,我是小悟问题和概念1、接口调用存在的问题现如今我们的系统大多拆分为分布式SOA,或者微服务,一套系统中包含了多个子系统服务,而一个子系统服务往往会去调用另一个服务,而......
  • javaSE-day03(常用API一)
    API(ApplicationProgramminginterface)意思是应用程序编程接口,说人话就是Java帮我们写好的一些程序,如:类、方法等,我们直接拿过来用就可以解决一些问题。可以在API文档进行查......
  • JavaScript Scope All In One
    JavaScriptScopeAllInOneGlobalScope/全局作用域ScriptScope/脚本作用域LocalScope/局部作用域(函数作用域)BlockScope/块级作用域({}作用域)C......
  • Java入门_一维数组_第一题_升序数组
    声明咱是个新手,没啥技术只会最基础的,见谅哈。更简化的方法还请大佬指教。题目:已知有个升序数组的数组,要插入一个元素,该数组顺序依然是升序。例如:{25,49,74,......
  • java.io.IOException: Packet len1213486160 is out of range!
    部署otter,启动node的时候一直报错:2023-03-2110:39:24.615[main-SendThread(10.224.250.251:8080)]WARNorg.apache.zookeeper.ClientCnxn-Session0x0forserver......
  • java发送验证码
    导入maven包(commons-email)<dependency><groupId>org.apache.commons</groupId><artifactId>commons-email</artifactId><version>1.5</version></......
  • Java 获取最近12个月数据,包含当月数据
    List<String>dateList=newArrayList<>();Calendarcalendar=Calendar.getInstance();//月份最大为11最小为0所以设置初始月份时加1,需要获取到当前月份所以设置......