-
使用泛型(推荐)
-
public interface FlowHandlerGateway<P extends FlowApprovalPageCondition> {Page<FlowApprovalPage> pageQuery(P condition); }//父类 @Slf4j @Component @RequiredArgsConstructor public class FlowHandlerGatewayImpl<P extends FlowApprovalPageCondition> implements FlowHandlerGateway<P>{private final FlowApprovalWrapper flowApprovalWrapper;private final InfraConverter converter;private final CommonAdapter commonAdapter;@Overridepublic Page<FlowApprovalPage> pageQuery(P condition) {Page<FlowApprovalPO> pageInfo = commonAdapter.toPage(condition);LambdaQueryWrapper<FlowApprovalPO> wrapper = Wrappers.<FlowApprovalPO>lambdaQuery().eq(StrUtil.isNotBlank(condition.getBizNo()), FlowApprovalPO::getBizNo, condition.getBizNo()).eq(StrUtil.isNotBlank(condition.getFlowType()), FlowApprovalPO::getFlowType, condition.getFlowType()).eq(ObjectUtil.isNotNull(condition.getInstanceId()), FlowApprovalPO::getInstanceId, condition.getInstanceId()).eq(StrUtil.isNotBlank(condition.getFlowStatus()), FlowApprovalPO::getFlowStatus, condition.getFlowStatus()).eq(ObjectUtil.isNotNull(condition.getApplyBy()), FlowApprovalPO::getApplyBy, condition.getApplyBy()).like(StrUtil.isNotBlank(condition.getApplyByName()), FlowApprovalPO::getApplyByName, condition.getApplyByName()).ge(ObjectUtil.isNotNull(condition.getApplyStartTime()), FlowApprovalPO::getCreatedAt, condition.getApplyStartTime()).le(ObjectUtil.isNotNull(condition.getApplyEndTime()), FlowApprovalPO::getCreatedAt, condition.getApplyEndTime());// Wrapper拓展方法pageQueryWrapperExpand(wrapper, condition);return converter.toFlowApprovalPage(flowApprovalWrapper.page(pageInfo, wrapper));}protected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, P conditionPage){} }/*** 子类*/ @Component @Slf4j public class SimulationLoginGatewayImpl extends FlowHandlerGatewayImpl<SimulationLoginPageCondition>{public SimulationLoginGatewayImpl(FlowApprovalWrapper flowApprovalWrapper, InfraConverter converter, CommonAdapter commonAdapter) {super(flowApprovalWrapper, converter, commonAdapter);}@Overrideprotected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, SimulationLoginPageCondition condition){wrapper.apply(ObjectUtil.isNotNull(condition.getTenantId()),"biz_data->>'$.tenant_id' = {0}", condition.getTenantId()).apply(ObjectUtil.isNotNull(condition.getApplicationId()),"biz_data->>'$.application_id' = {0}", condition.getApplicationId()).apply(ObjectUtil.isNotNull(condition.getEnableStatus()),"biz_data->>'$.enable_status' = {0}", condition.getEnableStatus()).apply(ObjectUtil.isNotNull(condition.getEmail()),"biz_data->>'$.email' = {0}", condition.getEmail());}}
-
-
通过强制类型转换。其中SimulationLoginPageCondition实体继承FlowApprovalPageCondition实体,子类SimulationLoginPageCondition为什么不能使用多态特性,直接重写父类pageQueryWrapperExpand方法
-
// 父类
@Slf4j @Component @RequiredArgsConstructor public class FlowHandlerGatewayImpl implements FlowHandlerGateway{private final FlowApprovalWrapper flowApprovalWrapper;private final InfraConverter converter;private final CommonAdapter commonAdapter;@Overridepublic <P extends FlowApprovalPageCondition> Page<FlowApprovalPage> pageQuery(P condition) {Page<FlowApprovalPO> pageInfo = commonAdapter.toPage(condition);LambdaQueryWrapper<FlowApprovalPO> wrapper = Wrappers.<FlowApprovalPO>lambdaQuery().eq(StrUtil.isNotBlank(condition.getBizNo()), FlowApprovalPO::getBizNo, condition.getBizNo()).eq(StrUtil.isNotBlank(condition.getFlowType()), FlowApprovalPO::getFlowType, condition.getFlowType()).eq(ObjectUtil.isNotNull(condition.getInstanceId()), FlowApprovalPO::getInstanceId, condition.getInstanceId()).eq(StrUtil.isNotBlank(condition.getFlowStatus()), FlowApprovalPO::getFlowStatus, condition.getFlowStatus()).eq(ObjectUtil.isNotNull(condition.getApplyBy()), FlowApprovalPO::getApplyBy, condition.getApplyBy()).like(StrUtil.isNotBlank(condition.getApplyByName()), FlowApprovalPO::getApplyByName, condition.getApplyByName()).ge(ObjectUtil.isNotNull(condition.getApplyStartTime()), FlowApprovalPO::getCreatedAt, condition.getApplyStartTime()).le(ObjectUtil.isNotNull(condition.getApplyEndTime()), FlowApprovalPO::getCreatedAt, condition.getApplyEndTime());// Wrapper拓展方法pageQueryWrapperExpand(wrapper, condition);return converter.toFlowApprovalPage(flowApprovalWrapper.page(pageInfo, wrapper));}protected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, FlowApprovalPageCondition conditionPage){} }/**
* 子类
*/
@Component
@Slf4j
public class SimulationLoginGatewayImpl extends FlowHandlerGatewayImpl{
public SimulationLoginGatewayImpl(FlowApprovalWrapper flowApprovalWrapper, InfraConverter converter, CommonAdapter commonAdapter) {
super(flowApprovalWrapper, converter, commonAdapter);
}
// 出现报错,重写方法参数和父类不一样、违反了java规范
@Override
protected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, SimulationLoginPageCondition condition){
wrapper.apply(ObjectUtil.isNotNull(condition.getTenantId()),"biz_data->>'$.tenant_id' = {0}", condition.getTenantId())
.apply(ObjectUtil.isNotNull(condition.getApplicationId()),"biz_data->>'$.application_id' = {0}", condition.getApplicationId())
.apply(ObjectUtil.isNotNull(condition.getEnableStatus()),"biz_data->>'$.enable_status' = {0}", condition.getEnableStatus())
.apply(ObjectUtil.isNotNull(condition.getEmail()),"biz_data->>'$.email' = {0}", condition.getEmail());
}// 强制类型转换,子类中重写父类方法,也不推荐后面有原因,违反了开闭原则,下面有讲解 @Override protected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, FlowApprovalPageCondition condition) {// 强制类型转换SimulationLoginPageCondition simCondition = (SimulationLoginPageCondition) condition;// 使用转换后的条件wrapper.apply(ObjectUtil.isNotNull(simCondition.getTenantId()),"biz_data->>'$.tenant_id' = {0}", simCondition.getTenantId()).apply(ObjectUtil.isNotNull(simCondition.getApplicationId()),"biz_data->>'$.application_id' = {0}", simCondition.getApplicationId()).apply(ObjectUtil.isNotNull(simCondition.getEnableStatus()),"biz_data->>'$.enable_status' = {0}", simCondition.getEnableStatus()).apply(ObjectUtil.isNotNull(simCondition.getEmail()),"biz_data->>'$.email' = {0}", simCondition.getEmail()); }
}
-
-
原理
-
1. 单一职责原则 (Single Responsibility Principle - SRP)
核心思想:一个类应该只有一个引起它变化的原因。换句话说,一个类应该只负责一项职责。
在Java中的理解:
-
好处:类的职责越单一,它的内聚性就越高,就越容易被理解、维护和修改。修改一个功能不会意外影响到其他不相关的功能。
-
** violation (违反)的例子**:如果一个类既负责数据库操作,又负责业务逻辑计算,还负责发送邮件,那它就违反了SRP。此时,修改数据库连接方式、业务算法或邮件服务器配置都会修改这个类,风险很高。
结合你的代码:
-
FlowHandlerGatewayImpl
的职责非常明确:构建查询流程审批单的分页条件。它不关心具体的SQL执行(由FlowApprovalWrapper
负责),也不关心PO到Domain的转换(由InfraConverter
负责)。它通过依赖注入将其他职责委托给了专门的类,这很好地遵循了SRP。
2. 开闭原则 (Open/Closed Principle - OCP)
核心思想:软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。
在Java中的理解:
-
“对修改关闭”:意味着一个已经完成并测试通过的类的核心代码不应该再被修改。
-
“对扩展开放”:意味着当有新的需求时,你应该能够通过扩展这个类(如通过继承、组合、实现接口等方式)来添加新功能,而不是修改它。
-
实现手段:抽象(接口、抽象类)和多态是实现OCP的关键。
结合你的代码:
-
FlowHandlerGatewayImpl
的pageQueryWrapperExpand
方法是一个空实现(钩子方法)。这本身就是为扩展留下的“窗口”。 -
当需要为
SimulationLoginPageCondition
添加特殊的查询条件时,你没有修改FlowHandlerGatewayImpl
的核心pageQuery
方法,而是扩展了它,创建了SimulationLoginGatewayImpl
子类并重写了pageQueryWrapperExpand
方法。 -
这正是对扩展开放,对修改关闭的完美体现。父类代码稳定,新功能通过子类扩展实现。
3. 里氏替换原则 (Liskov Substitution Principle - LSP)
核心思想:所有引用基类(父类)的地方必须能透明地使用其子类的对象,而程序的行为不会发生变化。
在Java中的理解:
-
子类可以扩展父类的功能,但不能改变父类原有的功能和行为约定(如方法签名、返回值、异常抛出等)。
-
子类不应该比父类有更严格的前置条件(比如,父类方法参数是
Integer
,子类重写时却要求参数必须大于0,这就违反了LSP)。 -
** violation (违反)的例子:这正是你之前遇到的问题。如果强行在子类中将
FlowApprovalPageCondition
参数转换为SimulationLoginPageCondition
,那么该子类对象就无法透明替换父类对象。因为父类方法可以接受任何FlowApprovalPageCondition
,而你的子类方法实际上只接受特定的子类型,传入其他类型会导致ClassCastException
,行为被破坏了**。
正确的做法(结合你的代码):
-
使用泛型来保证类型安全,从而遵守LSP。
// 父类 public class FlowHandlerGatewayImpl<C extends FlowApprovalPageCondition> {public Page<FlowApprovalPage> pageQuery(C condition) { ... }protected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, C condition) {} }// 子类 public class SimulationLoginGatewayImpl extends FlowHandlerGatewayImpl<SimulationLoginPageCondition> {@Overrideprotected void pageQueryWrapperExpand(LambdaQueryWrapper<FlowApprovalPO> wrapper, SimulationLoginPageCondition condition) {// 这里直接使用SimulationLoginPageCondition,无需强制转换,且类型绝对安全} }
-
现在,任何期望使用
FlowHandlerGatewayImpl<SimulationLoginPageCondition>
的地方,都可以安全地用SimulationLoginGatewayImpl
来替换,因为子类方法完全满足父类方法的契约(参数是SimulationLoginPageCondition
,它是FlowApprovalPageCondition
的子类),行为一致且不会出错。
4. 扩展性 (Extensibility)
核心思想:软件系统能够容易地适应新需求、添加新功能,而所需的工作量和成本最低,且对现有系统的影响最小。
在Java中的理解:
-
扩展性不是某个单一原则,而是良好应用上述所有原则(SRP, OCP, LSP)以及依赖倒置、接口隔离等原则后的自然结果。
-
一个高扩展性的系统,其结构是松耦合的,通过抽象和接口定义契约,使得添加新模块就像“插拔组件”一样简单。
结合你的代码:
-
你目前的设计(尤其是使用泛型重构后)具有很高的扩展性。
-
如何添加一个新的流程类型(如
DataExportPageCondition
)?-
创建新的条件类:
DataExportPageCondition extends FlowApprovalPageCondition
。 -
创建新的网关子类:
DataExportGatewayImpl extends FlowHandlerGatewayImpl<DataExportPageCondition>
。 -
重写扩展方法:在子类中重写
pageQueryWrapperExpand
,添加数据导出特有的查询逻辑。
-
-
你做到了什么?
-
没有修改任何现有的、稳定的父类代码(
FlowHandlerGatewayImpl
) -> 遵循OCP。 -
新功能在独立的、职责单一的新类中完成 -> 遵循SRP。
-
新的子类可以完全替换父类,行为一致 -> 遵循LSP。
-
整个过程的代价极小,风险极低,因为只是添加新代码而不是修改老代码。
-
总结
原则 核心思想 在示例中的体现(良好设计后) 单一职责 (SRP) 一个类只干一件事 FlowHandlerGatewayImpl
只负责构建查询Wrapper,其他职责外包。开闭原则 (OCP) 对扩展开放,对修改关闭 通过泛型和可重写的钩子方法,允许无限扩展新的查询条件类型,而无需修改基类。 里氏替换 (LSP) 子类必须能透明替换父类 使用泛型后,子类 SimulationLoginGatewayImpl
可以安全替换FlowHandlerGatewayImpl<SimulationLoginPageCondition>
。扩展性 容易添加新功能 添加新流程类型非常简单、快速且安全,是应用上述原则后的必然结果。 这些原则共同作用,指导我们构建出健壮、灵活、易于维护和扩展的Java应用程序。你代码中最初遇到的问题,正是因为没有很好地满足LSP,进而影响了扩展的优雅性。通过引入泛型,你同时完美地践行了SRP、OCP和LSP,最终获得了极高的扩展性。
-
-