
优化相似请求对象的通用处理逻辑
在软件开发中,我们经常会遇到这样的场景:多个数据传输对象(dto)或请求对象在结构上高度相似,并且需要经过相同的业务逻辑处理,例如数据验证。如果为每个相似的请求对象都编写一个独立的处理方法,即使这些方法的内部逻辑完全相同,也会导致大量的代码重复,降低代码的可维护性和可读性。
考虑以下两个Java record 类型,它们分别用于创建和更新对象:
public record CreateObjectRequest (
CustomObjectA a,
CustomObjectB b,
CustomObjectC c
) {}
public record UpdateObjectRequest (
CustomObjectA a,
CustomObjectB b
) {}如果我们需要对这两个请求对象执行相同的验证逻辑,一个常见的做法是为它们分别创建重载的验证方法:
public class RequestValidator {
public void validateRequest(CreateObjectRequest createObjectRequest) {
// 大量相同的验证逻辑...
System.out.println("Validating CreateObjectRequest...");
// 示例:检查a和b是否为空
if (createObjectRequest.a() == null) {
throw new IllegalArgumentException("CustomObjectA cannot be null for CreateObjectRequest.");
}
if (createObjectRequest.b() == null) {
throw new IllegalArgumentException("CustomObjectB cannot be null for CreateObjectRequest.");
}
// ... 其他验证
}
public void validateRequest(UpdateObjectRequest updateObjectRequest) {
// 同样大量的验证逻辑...
System.out.println("Validating UpdateObjectRequest...");
// 示例:检查a和b是否为空
if (updateObjectRequest.a() == null) {
throw new IllegalArgumentException("CustomObjectA cannot be null for UpdateObjectRequest.");
}
if (updateObjectRequest.b() == null) {
throw new IllegalArgumentException("CustomObjectB cannot be null for UpdateObjectRequest.");
}
// ... 其他验证
}
}这种方法虽然功能上可行,但显然存在代码冗余。当验证逻辑变得复杂或需要修改时,必须在多个地方进行同步更新,极易出错。
利用抽象父类实现代码复用
为了解决上述问题,我们可以利用Java的面向对象特性,特别是抽象类和继承,来消除这种冗余。核心思想是识别出所有相似请求对象共有的字段和行为,并将它们抽象到一个共同的父类中。
立即学习“Java免费学习笔记(深入)”;
1. 定义抽象父类
首先,创建一个抽象父类,例如 ObjectRequest,它包含所有子请求类型共享的字段。
// 假设 CustomObjectA 和 CustomObjectB 的基类或接口是 CustomObject
public abstract class ObjectRequest {
protected CustomObject a;
protected CustomObject b;
// 构造函数、getter/setter(如果需要)
public CustomObject getA() {
return a;
}
public CustomObject getB() {
return b;
}
}注意事项:
- 这里将 CustomObjectA 和 CustomObjectB 统一为 CustomObject,前提是它们具有共同的父类或接口,或者我们可以通过类型擦除来处理。在实际应用中,如果 CustomObjectA 和 CustomObjectB 是完全独立的类型但只是字段名相同,则需要考虑更复杂的泛型或接口方案。但根据问题描述,它们是“同样的字段”,暗示可以抽象。
- 字段声明为 protected 或 public,以便子类可以访问或通过 getter 方法提供访问。
2. 子类继承抽象父类
接下来,让具体的请求对象(如 CreateObjectRequest 和 UpdateObjectRequest)继承这个抽象父类。由于Java record 类型不能直接继承类,我们可以将 record 改为普通的 class,或者在 record 内部通过组合来引用父类的逻辑(但这样就失去了继承带来的多态便利)。为了实现继承带来的多态性,我们通常会选择将 record 转换为普通 class。
// CreateObjectRequest 继承 ObjectRequest
public class CreateObjectRequest extends ObjectRequest {
private CustomObjectC c; // CreateObjectRequest 特有的字段
public CreateObjectRequest(CustomObjectA a, CustomObjectB b, CustomObjectC c) {
this.a = a; // 从父类继承的字段
this.b = b; // 从父类继承的字段
this.c = c;
}
public CustomObjectC getC() {
return c;
}
}
// UpdateObjectRequest 继承 ObjectRequest
public class UpdateObjectRequest extends ObjectRequest {
// UpdateObjectRequest 没有特有的字段,或者有其他特有字段
public UpdateObjectRequest(CustomObjectA a, CustomObjectB b) {
this.a = a; // 从父类继承的字段
this.b = b; // 从父类继承的字段
}
}注意: 这里的 CustomObjectA 和 CustomObjectB 类型在 ObjectRequest 中被统一为 CustomObject。如果 CustomObjectA 和 CustomObjectB 本身不是 CustomObject 的子类,但它们都具有某些共同的属性,那么 ObjectRequest 中的 a 和 b 可以声明为它们的共同接口或基类类型。为了简化,这里假设 CustomObjectA 和 CustomObjectB 都可以被 CustomObject 类型引用。
3. 重构通用处理方法
现在,验证方法只需要接受抽象父类 ObjectRequest 作为参数。通过多态性,这个方法就可以处理所有继承自 ObjectRequest 的子类实例。
public class RequestValidator {
public void validateRequest(ObjectRequest objectRequest) {
// 通用的验证逻辑,现在只需要编写一次
System.out.println("Validating generic ObjectRequest...");
// 访问父类中的共享字段
if (objectRequest.getA() == null) {
throw new IllegalArgumentException("CustomObjectA cannot be null.");
}
if (objectRequest.getB() == null) {
throw new IllegalArgumentException("CustomObjectB cannot be null.");
}
// 如果需要针对特定子类进行额外验证,可以使用 instanceof
if (objectRequest instanceof CreateObjectRequest createRequest) {
System.out.println("Performing CreateObjectRequest specific validation.");
if (createRequest.getC() == null) {
throw new IllegalArgumentException("CustomObjectC cannot be null for CreateObjectRequest.");
}
} else if (objectRequest instanceof UpdateObjectRequest updateRequest) {
System.out.println("Performing UpdateObjectRequest specific validation.");
// 针对 UpdateObjectRequest 的特有验证
}
// ... 其他通用验证
}
}通过这种方式,我们成功地将重复的验证逻辑集中到一个方法中。当需要修改或扩展通用验证逻辑时,只需修改 validateRequest(ObjectRequest objectRequest) 方法即可,大大提高了代码的可维护性和可扩展性。
总结与最佳实践
利用抽象父类来优化通用方法是Java中一种强大的代码复用模式。它适用于以下场景:
- 多个类共享大量相同的字段和行为:这些类在概念上属于同一类事物,但有各自的特有属性。
- 需要对这些相似类执行相同的操作:例如验证、序列化、转换等。
优点:
- 减少代码冗余:避免重复编写相同的业务逻辑。
- 提高可维护性:逻辑修改只需在一个地方进行。
- 增强可扩展性:新增的相似请求类型只需继承抽象父类,即可自动获得通用处理能力。
- 改善可读性:代码结构更清晰,意图表达更明确。
注意事项:
- 过度抽象:如果类之间的共同点非常少,或者强行将不相关的类抽象到一起,可能会导致设计过于复杂,反而降低可读性。
- 继承的局限性:Java是单继承语言,如果一个类已经继承了其他类,就不能再通过继承这种方式来复用通用逻辑。此时可以考虑接口结合默认方法、组合模式或泛型等其他设计模式。
- record 类型的限制:record 类型在Java 16+中引入,其设计目标是作为不可变的数据载体,不能继承其他类(但可以实现接口)。因此,如果需要利用继承实现多态,可能需要将 record 转换为传统的 class。
通过合理运用抽象和继承,开发者可以编写出更优雅、更健壮、更易于维护的Java代码。










