
在典型的三层或多层架构中,controller层主要负责接收外部请求、协调处理流程并将结果返回。然而,在实际开发中,我们常常会发现controller层承担了过多的职责,例如:
以上职责的混合导致Controller方法内部出现大量重复的映射代码和固定的调用模式,降低了代码的可读性和可维护性,也使得单元测试变得复杂。例如,以下代码片段展示了这种常见的痛点:
public class Controller {
private Mapper mapper; // 假设有一个通用的对象映射器
private Service1 service1;
private Service2 service2;
public Response1 test1(Request1 request1){
ServiceInputDto1 serviceInputDto1 = mapper.map(request1, ServiceInputDto1.class);
ServiceOutputDto1 serviceOutputDto1 = service1.test(serviceInputDto1);
Response1 response1 = mapper.map(serviceOutputDto1, Response1.class);
return response1;
}
public Response2 test2(Request2 request2){
ServiceInputDto2 serviceInputDto2 = mapper.map(request2, ServiceInputDto2.class);
ServiceOutputDto2 serviceOutputDto2 = service2.test(serviceInputDto2);
Response2 response2 = mapper.map(serviceOutputDto2, Response2.class);
return response2;
}
}可以看到,test1和test2方法中,DTO映射和业务服务调用的模式是高度重复的。
为了解决上述问题,引入一个介于Controller和业务服务之间的抽象层是十分必要的。这个中间层的主要目标是:
关于这个中间层可以采用的设计模式,有以下几种考量:
在上述示例场景中,由于主要重复的是DTO映射和单一业务服务调用的模式,一个通用映射与调用处理器的方案更为契合,它能以更简洁的方式解决代码重复问题。
我们可以设计一个名为InputOutputMapping的通用类来封装DTO映射和业务服务调用的通用逻辑。这个类将利用泛型和函数式接口来提供高度的灵活性和复用性。
InputOutputMapping类定义:
import java.util.function.Function;
public class InputOutputMapping {
private Mapper mapper; // 假设Mapper是一个通用的对象映射器,例如MapStruct或ModelMapper
public InputOutputMapping(Mapper mapper) {
this.mapper = mapper;
}
/**
* 泛型方法,用于封装请求到DTO的映射、服务调用以及DTO到响应的映射。
*
* @param requestObject 原始请求对象 (REQ)
* @param inDtoClass 业务服务输入DTO的Class类型 (IN_DTO)
* @param serviceFunction 业务服务函数,接收IN_DTO并返回OUT_DTO (Function<IN_DTO, OUT_DTO>)
* @param responseClass 最终响应对象的Class类型 (RESP)
* @param <REQ> 请求对象的类型
* @param <IN_DTO> 业务服务输入DTO的类型
* @param <OUT_DTO> 业务服务输出DTO的类型
* @param <RESP> 最终响应对象的类型
* @return 映射后的响应对象
*/
public <REQ, IN_DTO, OUT_DTO, RESP> RESP apply(
REQ requestObject,
Class<IN_DTO> inDtoClass,
Function<IN_DTO, OUT_DTO> serviceFunction,
Class<RESP> responseClass
) {
// 1. 将请求对象映射为业务服务输入DTO
final IN_DTO inputDto = mapper.map(requestObject, inDtoClass);
// 2. 调用业务服务
final OUT_DTO outputDto = serviceFunction.apply(inputDto);
// 3. 将业务服务输出DTO映射为最终响应对象
final RESP response = mapper.map(outputDto, responseClass);
return response;
}
}Controller层改造:
引入InputOutputMapping后,Controller层的代码将变得非常简洁和专注于其核心职责——接收请求和返回响应,而将复杂的映射和调用逻辑委托给InputOutputMapping。
import java.util.function.Function; // 确保导入Function
public class Controller {
private Service1 service1;
private Service2 service2;
private InputOutputMapping mapping; // 注入InputOutputMapping实例
public Controller(Service1 service1, Service2 service2, InputOutputMapping mapping) {
this.service1 = service1;
this.service2 = service2;
this.mapping = mapping;
}
public Response1 test1(Request1 request1){
return mapping.apply(
request1,
ServiceInputDto1.class,
serviceInputDto1 -> service1.test(serviceInputDto1), // 使用Lambda表达式传递服务调用逻辑
Response1.class
);
}
public Response2 test2(Request2 request2){
return mapping.apply(
request2,
ServiceInputDto2.class,
serviceInputDto2 -> service2.test(serviceInputDto2),
Response2.class
);
}
}主要优势:
注意事项:
通过引入一个通用的InputOutputMapping层,我们有效地将Controller层的DTO映射和业务服务调用逻辑进行了抽象和封装,极大地提升了代码的整洁度、可维护性和可测试性。这种模式使得Controller层更加专注于其核心职责,避免了代码重复,并为后续的功能扩展和维护打下了坚实的基础。在设计时,应权衡其带来的优势和潜在的复杂性,并结合具体的业务场景和项目规模进行决策。
以上就是优化Controller层:引入DTO映射与服务调用抽象层的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号