设计模式之-访问者模式

一、定义

访问者模式(Visitor Pattern)是一种行为型设计模式,它将作用于某对象结构的操作封装到独立的类中,使得在不改变对象结构的前提下添加新的操作变得更加容易

本质:让操作和结构解耦,通过“双分派”机制在访问者中实现针对不同类型元素的处理逻辑。

二、适用场景

  • 有一个对象结构(如 AST、表单、订单、规则树等),包含多种类型元素;
  • 想对结构中不同类型的元素执行不同操作(如校验、导出、计算、转换等);
  • 希望频繁添加操作,而不是修改元素类本身;
  • 元素结构比较稳定,但操作种类经常变化。

三、核心角色

角色

说明

Visitor

抽象访问者,声明访问每个元素的方法

ConcreteVisitor

具体访问者,实现访问逻辑(如计算、打印、校验等)

Element

抽象元素,定义 accept(Visitor)接口

ConcreteElement

具体元素,实现 accept 并将自身传递给访问者的对应方法

ObjectStructure

元素集合类,负责管理元素并接受访问者

四、业务背景示例:保险理赔系统规则引擎

在保险理赔系统中,不同的保单类型(医疗险、意外险、财产险)有不同的计算规则,我们希望可以在不修改保单类型类的前提下:

  • 增加理赔金额计算逻辑
  • 增加打印规则报告
  • 增加风控规则验证

使用访问者模式非常适合!

五、代码实现

1. 抽象元素接口(Element 角色)

// 所有保单的抽象接口,定义接受访问者的入口
public interface PolicyElement {
    void accept(PolicyVisitor visitor);
}

2. 具体元素类(ConcreteElement 角色)

// 医疗保险保单
public class MedicalPolicy implements PolicyElement {
    private double baseAmount;

    public MedicalPolicy(double baseAmount) {
        this.baseAmount = baseAmount;
    }

    public double getBaseAmount() {
        return baseAmount;
    }

    @Override
    public void accept(PolicyVisitor visitor) {
        visitor.visitMedical(this);
    }
}

// 意外保险保单
public class AccidentPolicy implements PolicyElement {
    private int accidentLevel;

    public AccidentPolicy(int accidentLevel) {
        this.accidentLevel = accidentLevel;
    }

    public int getAccidentLevel() {
        return accidentLevel;
    }

    @Override
    public void accept(PolicyVisitor visitor) {
        visitor.visitAccident(this);
    }
}

// 财产保险保单
public class PropertyPolicy implements PolicyElement {
    private double damagePercent;

    public PropertyPolicy(double damagePercent) {
        this.damagePercent = damagePercent;
    }

    public double getDamagePercent() {
        return damagePercent;
    }

    @Override
    public void accept(PolicyVisitor visitor) {
        visitor.visitProperty(this);
    }
}

3. 抽象访问者接口(Visitor 角色)

public interface PolicyVisitor {
    void visitMedical(MedicalPolicy policy);
    void visitAccident(AccidentPolicy policy);
    void visitProperty(PropertyPolicy policy);
}

4. 具体访问者:理赔金额计算(ConcreteVisitor 角色)

// 实现理赔金额的计算规则
public class ClaimAmountCalculator implements PolicyVisitor {
    private double totalAmount = 0;

    public double getTotalAmount() {
        return totalAmount;
    }

    @Override
    public void visitMedical(MedicalPolicy policy) {
        double amount = policy.getBaseAmount() * 0.8; // 医疗赔80%
        System.out.println("[医疗险] 理赔金额: " + amount);
        totalAmount += amount;
    }

    @Override
    public void visitAccident(AccidentPolicy policy) {
        double amount = 1000 * policy.getAccidentLevel(); // 每等级赔1000元
        System.out.println("[意外险] 理赔金额: " + amount);
        totalAmount += amount;
    }

    @Override
    public void visitProperty(PropertyPolicy policy) {
        double amount = 50000 * policy.getDamagePercent(); // 最高赔5万,按比例
        System.out.println("[财产险] 理赔金额: " + amount);
        totalAmount += amount;
    }
}

5. 对象结构类(ObjectStructure 角色)

// 保单集合,统一接受访问者
public class PolicyBook {
    private List<PolicyElement> policies = new ArrayList<>();

    public void addPolicy(PolicyElement policy) {
        policies.add(policy);
    }

    public void accept(PolicyVisitor visitor) {
        for (PolicyElement policy : policies) {
            policy.accept(visitor);
        }
    }
}

6. 客户端调用(Client)

public class VisitorDemo {
    public static void main(String[] args) {
        // 构建保单列表
        PolicyBook policyBook = new PolicyBook();
        policyBook.addPolicy(new MedicalPolicy(10000));
        policyBook.addPolicy(new AccidentPolicy(3));
        policyBook.addPolicy(new PropertyPolicy(0.6));

        // 理赔金额访问者
        ClaimAmountCalculator calculator = new ClaimAmountCalculator();
        policyBook.accept(calculator);

        System.out.println("总理赔金额:" + calculator.getTotalAmount());
    }
}

六、类与角色对照表

类名

模式角色

说明

PolicyVisitor

Visitor

抽象访问者接口

ClaimAmountCalculator

ConcreteVisitor

理赔金额计算访问者

PolicyElement

Element

抽象保单元素接口

MedicalPolicy

ConcreteElement

医疗保险

AccidentPolicy

ConcreteElement

意外保险

PropertyPolicy

ConcreteElement

财产保险

PolicyBook

ObjectStructure

保单集合结构

VisitorDemo

Client

客户端,执行访问者逻辑

七、优缺点分析(Pros & Cons)

✅ 优点

优点

说明

扩展性强

新增操作只需添加访问者类,不改动元素类

职责分离清晰

操作封装在访问者中,元素关注数据本身

支持双分派

根据访问者和元素的类型决定最终方法调用,避免大量 if/instanceof 判断

❌ 缺点

缺点

说明

元素类变更影响大

添加新元素,需要修改所有访问者类

破坏封装性

访问者可能访问元素内部状态,破坏其封装性

维护成本高

访问者与元素类之间强耦合,每增加一种结构或行为都要成对修改

八、真实业务使用场景

场景

应用说明

报表系统

多种报表对结构相同的数据生成不同格式(Excel/PDF/HTML)

税务计算

针对不同类型产品或用户计算不同税率

保险理赔规则

本文示例:针对不同保单类型应用不同计算逻辑

编译器语法树遍历

不同的语法节点交给不同访问者生成中间代码、语义分析等

产品导出

电商商品导出多种格式(JSON、XML、YAML)

九、总结(Summary)

访问者模式通过“操作抽离”的方式,使得在不修改原始对象结构的情况下就可以增加新行为。它非常适用于结构稳定、行为频繁变化的系统,如规则引擎、报表系统、导出工具等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码蚁Q

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值