最近我在重构公司的保险系统,遇到个头疼的问题:不同类型的保险(医疗险、意外险、寿险)既有共同的业务逻辑(比如保单生成、理赔申请),又有各自独特的规则(比如理赔材料要求、费率计算方式)。
一开始我全用if else堆逻辑,代码像坨shi。后来用接口和抽象类重构后,代码瞬间清爽了,架构师看完直接给我涨了20%的绩效!今天就用这个真实案例,给你讲透接口和抽象类的实战用法。
场景还原:保险系统的痛点
公司的保险系统支持三种险种:
- 医疗险:理赔时需要医院诊断证明、发票;
- 意外险:理赔时需要事故证明、伤残鉴定;
- 寿险:理赔时需要死亡证明、受益人关系证明。
原来的代码是这样的:
public class InsuranceService {
public void claim(String insuranceType, ClaimRequest request) {
if ("HEALTH".equals(insuranceType)) {
// 医疗险理赔逻辑:校验医院诊断证明、发票
System.out.println("校验医疗险理赔材料:诊断证明、发票");
System.out.println("计算医疗险理赔金额...");
System.out.println("医疗险理赔审批中...");
} else if ("ACCIDENT".equals(insuranceType)) {
// 意外险理赔逻辑:校验事故证明、伤残鉴定
System.out.println("校验意外险理赔材料:事故证明、伤残鉴定");
System.out.println("计算意外险理赔金额...");
System.out.println("意外险理赔审批中...");
} else if ("LIFE".equals(insuranceType)) {
// 寿险理赔逻辑:校验死亡证明、受益人关系证明
System.out.println("校验寿险理赔材料:死亡证明、受益人关系证明");
System.out.println("计算寿险理赔金额...");
System.out.println("寿险理赔审批中...");
}
}
}
问题很明显:
- 每新增一种险种,就要改这个if else,违反开闭原则;
- 三种险种的理赔流程有重复代码(比如计算金额、审批),没有复用;
- 代码可读性差,逻辑分散,维护困难。
用接口重构:定义“规矩”
首先想到用接口,因为所有保险都得能“理赔”:
// 保险接口:定义理赔规矩
public interface Insurance {
// 校验理赔材料(具体怎么校验,由实现类决定)
boolean validateClaimDocuments(List<Document> documents);
// 计算理赔金额(不同险种算法不同)
BigDecimal calculateClaimAmount(ClaimRequest request);
// 提交理赔申请
ClaimResult submitClaim(ClaimRequest request);
}
// 医疗险实现
public class HealthInsurance implements Insurance {
@Override
public boolean validateClaimDocuments(List<Document> documents) {
System.out.println("校验医疗险理赔材料:诊断证明、发票");
return documents.stream()
.anyMatch(doc -> "诊断证明".equals(doc.getType()) || "发票".equals(doc.getType()));
}
@Override
public BigDecimal calculateClaimAmount(ClaimRequest request) {
System.out.println("按医疗险规则计算理赔金额...");
return request.getAmount().multiply(new BigDecimal("0.8")); // 假设报80%
}
@Override
public ClaimResult submitClaim(ClaimRequest request) {
// 提交理赔申请的通用逻辑
System.out.println("医疗险理赔申请已提交,等待审批...");
return new ClaimResult("PENDING", "审批中");
}
}
// 意外险和寿险的实现类似,省略...
这样改完后,新增险种只需要实现Insurance接口,不用改原有代码。但问题来了:三种险种的理赔申请提交逻辑(submitClaim方法)几乎一样,却要在每个实现类里重复写,这不是浪费吗?
用抽象类重构:提供“模板”
这时候抽象类就派上用场了!我们发现,虽然三种险种的理赔材料校验、金额计算不同,但理赔申请提交的流程是一样的(提交申请→生成理赔号→进入审批流程)。
于是我们把通用逻辑抽到抽象类里:
// 抽象保险类:实现通用逻辑,留下差异化部分给子类
public abstract class AbstractInsurance implements Insurance {
// 提交理赔申请的通用逻辑(模板方法)
@Override
public ClaimResult submitClaim(ClaimRequest request) {
// 1. 先校验材料(具体怎么校验由子类实现)
if (!validateClaimDocuments(request.getDocuments())) {
return new ClaimResult("REJECTED", "材料不全");
}
// 2. 计算理赔金额(具体怎么计算由子类实现)
BigDecimal amount = calculateClaimAmount(request);
// 3. 生成理赔号(通用逻辑)
String claimNumber = generateClaimNumber();
// 4. 记录理赔日志(通用逻辑)
logClaim(request, amount);
// 5. 返回结果(通用逻辑)
System.out.println("理赔申请已提交,理赔号:" + claimNumber);
return new ClaimResult("PENDING", "审批中");
}
// 生成理赔号(通用方法,子类不用重写)
private String generateClaimNumber() {
return UUID.randomUUID().toString().replace("-", "").substring(0, 10);
}
// 记录理赔日志(通用方法,子类不用重写)
private void logClaim(ClaimRequest request, BigDecimal amount) {
System.out.println("记录理赔日志:" + request.getPolicyNumber() + ",金额:" + amount);
}
}
// 医疗险只需继承抽象类,实现差异化方法
public class HealthInsurance extends AbstractInsurance {
@Override
public boolean validateClaimDocuments(List<Document> documents) {
System.out.println("校验医疗险理赔材料:诊断证明、发票");
return documents.stream()
.anyMatch(doc -> "诊断证明".equals(doc.getType()) || "发票".equals(doc.getType()));
}
@Override
public BigDecimal calculateClaimAmount(ClaimRequest request) {
System.out.println("按医疗险规则计算理赔金额...");
return request.getAmount().multiply(new BigDecimal("0.8"));
}
}
// 意外险和寿险类似,省略...
这样一来,三种险种的理赔申请流程被统一管理,重复代码大幅减少。子类只需要专注于自己特有的逻辑(校验材料、计算金额),通用的流程交给抽象类处理。
终极优化:抽象类+接口组合使用
实际项目中,我们还可以进一步优化:
- 用接口定义行为:比如所有保险都得能“理赔”“续保”;
- 用抽象类实现通用逻辑:比如理赔申请的提交流程、续保的基本逻辑;
- 具体险种继承抽象类并实现接口:兼顾灵活性和复用性。
// 保险接口:定义所有保险必须有的行为
public interface Insurance {
boolean validateClaimDocuments(List<Document> documents);
BigDecimal calculateClaimAmount(ClaimRequest request);
ClaimResult submitClaim(ClaimRequest request);
boolean renewPolicy(Policy policy);
}
// 抽象保险类:实现通用逻辑
public abstract class AbstractInsurance implements Insurance {
// 实现提交理赔申请的通用流程
@Override
public ClaimResult submitClaim(ClaimRequest request) {
// 通用流程...
}
// 实现续保的通用逻辑
@Override
public boolean renewPolicy(Policy policy) {
System.out.println("处理续保基本流程...");
if (policy.isExpired()) {
return false;
}
// 生成新保单、计算新保费等通用逻辑
return true;
}
}
// 医疗险:继承抽象类,实现自己特有的逻辑
public class HealthInsurance extends AbstractInsurance {
@Override
public boolean validateClaimDocuments(List<Document> documents) {
// 医疗险特有的材料校验
}
@Override
public BigDecimal calculateClaimAmount(ClaimRequest request) {
// 医疗险特有的金额计算
}
}
总结:保险项目里的接口与抽象类
- 接口:像“保险行业标准”,规定所有保险产品必须能“理赔”“续保”,但具体怎么做,由各保险公司(实现类)自己定。
- 抽象类:像“保险产品模板”,把所有保险都有的通用流程(比如理赔申请的提交步骤、续保的基本逻辑)实现好,子类只需要实现自己特有的部分。
口诀:
- 有“共同行为规范”,用接口;
- 有“共同实现逻辑”,用抽象类;
- 既有“规范”又有“通用实现”,接口+抽象类一起上!
下次遇到类似的业务场景,记得用这个套路重构代码——架构师看了会流泪,产品经理改需求时你能笑出声!
如果你是刚开始学习java,或者刚开始从事java行业,有很多的问题都可以关注微信公众号“java学长”,一个致力于打造免费指导学习java高薪就业的公益平台!


226

被折叠的 条评论
为什么被折叠?



