访问者模式

一、访问者模式

1、定义

访问者模式(Visitor Pattern)是一种将数据结构与数据操作分离的设计模式,指封装一些作用于某种数据结构中的各元素的操作,可以在不改变数据结构的前提下定义作用于这些元素的新的操作,属于行为型设计模式。

访问者模式的基本思想是,针对系统中拥有固定类型数的对象结构(元素),在其内提供一个 accept()方法来接受访问者对象的访问。不同的访问者对同一个元素的访问内容是不同,使得相同的元素集合可以产生不同的数据结果。

访问者模式又被称为最复杂的设计模式,使用频率不到。因为在开发中,我们很难找到数据结构不变化的情况。

访问者模式的核心是解耦数据结构与数据操作,使得对元素的操作具备优秀的扩展性。

2、结构

(1)模式的结构

主要角色如下:

  • 抽象访问者(IVisitor):接口或者抽象类,该类定义了一个 visit()方法用于访问每一个具体的元素,其参数就是具体元素对象。
  • 具体访问者(ConcreteVisitor):实现对具体元素的操作。
  • 抽象元素(IElement):接口或者抽象类,该类定义了一个接受访问者访问的方法 accept()方法,表示所有元素类型都支持被访问者访问。
  • 具体元素(ConcreteElement):具体元素类型,提供接受访问者的具体实现。通常的实现都为 visitor.visit(this)。
  • 结构对象(ObjectStructure):该类内部维护了元素集合,并提供方法接受访问者对该集合所有元素进行操作。

3、优缺点

优点:

  • 解耦了数据结构与数据操作,使得操作集合可以独立变化。
  • 可以通过扩展访问者角色,实现对数据集的不同操作,程序扩展性更好。
  • 元素具体类型并非单一,访问者均可操作。
  • 各角色职责分离,符合单一职责原则。

缺点:

  • 无法增加元素类型,会违背开闭原则。
  • 具体元素变更困难,修改的范围很大。
  • 违背依赖倒置原则:访问者角色依赖的是具体元素类型,而不是抽象。

4、使用场景

  • 数据结构稳定,作用于数据结构的操作经常变化的场景。
  • 需要数据结构与数据操作分离的场景。
  • 需要对不同数据类型(元素)进行操作,而不使用分支判断具体类型的场景

5、在框架源码中使用

  • Java的 NIO模块下的 FileVisitor接口。
  • Spring IoC中的 BeanDefinitionVisitor类。

二、模式的通用实现

代码如下:

public class VisitorPattern {

    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();

        IVisitor visitorA = new ConcreteVisitorA();
        objectStructure.showAccept(visitorA);

        System.out.println("====================");
        IVisitor visitorB = new ConcreteVisitorB();
        objectStructure.showAccept(visitorB);
    }
}

// 抽象访问者
interface IVisitor{
    void visit(ConcreteElementA element);

    void visit(ConcreteElementB element);
}

// 具体访问者
class ConcreteVisitorA implements IVisitor{
    @Override
    public void visit(ConcreteElementA element) {
        String result = element.operationA();
        System.out.println("ConcreteVisitorA handler -> result from " + element.getClass().getSimpleName() +", result = " + result);
    }

    @Override
    public void visit(ConcreteElementB element) {
        int result = element.operationB();
        System.out.println("ConcreteVisitorA handler -> result from " + element.getClass().getSimpleName() +", result = " + result);
    }
}

class ConcreteVisitorB implements IVisitor{
    @Override
    public void visit(ConcreteElementA element) {
        String result = element.operationA();
        System.out.println("ConcreteVisitorB handler -> result from " + element.getClass().getSimpleName() +", result = " + result);
    }

    @Override
    public void visit(ConcreteElementB element) {
        int result = element.operationB();
        System.out.println("ConcreteVisitorB handler -> result from " + element.getClass().getSimpleName() +", result = " + result);
    }
}

// 抽象元素
interface IElement{
    void accept(IVisitor visitor);
}

// 具体元素
class ConcreteElementA implements IElement{

    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }

    public String operationA(){
        return this.getClass().getSimpleName();
    }
}

class ConcreteElementB implements IElement{

    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }

    public int operationB(){
        return new Random().nextInt(100);
    }
}

// 结构对象
class ObjectStructure{
    private List<IElement> list = new ArrayList<>();

    {
        list.add(new ConcreteElementA());
        list.add(new ConcreteElementB());
    }

    // 遍历
    public void showAccept(IVisitor visitor){
        for (IElement element : this.list) {
            element.accept(visitor);
        }
    }
}

在这里插入图片描述

三、模式的应用实例

以 KPI考核的场景为例,领导查看员工的 KPI考核。

(1)员工基类 - 抽象元素

public abstract class Employee {

    protected String name;
    //员工 kpi数量
    protected int kpiCount;

    public Employee(String name) {
        this.name = name;
        this.kpiCount = new Random().nextInt(10);
    }

    public abstract void accept(IVisitor visitor);
}

(2)具体元素

public class Manager extends Employee {

	public Manager(String name) {
		super(name);
	}

	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}

	// 经理一年的产品量
	public int getProducts() {
		return new Random().nextInt(10);
	}
}

public class Engineer extends Employee{

    public Engineer(String name) {
        super(name);
    }

    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }

    //工程师一年的代码量
    public int getCodeLines(){
        return new Random().nextInt(10 * 10000);
    }
}

(3)抽象访问者

public interface IVisitor {

    //访问工程师类型
    void visit(Engineer engineer);

    //访问经理类型
    void visit(Manager manager);
}

(4)具体访问者

// CEO访问者
public class CEOVisitor implements IVisitor{

    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师:" + engineer.name + ",KPI: " + engineer.kpiCount);
    }

    @Override
    public void visit(Manager manager) {
        System.out.println("经理:" + manager.name + ",KPI: " + manager.kpiCount + ",产品数量:" + manager.getProducts());
    }
}

// CTO访问者
public class CTOVisitor implements IVisitor{

    @Override
    public void visit(Engineer engineer) {
        System.out.println("工程师:" + engineer.name + ",代码行数: " + engineer.getCodeLines());
    }

    @Override
    public void visit(Manager manager) {
        System.out.println("经理:" + manager.name + ",产品数量: " + manager.getProducts());
    }
}

(5)业务报表类 - 结构对象

public class BusinessReport {
    private List<Employee> employeeList = new ArrayList<>();

    {
        employeeList.add(new Manager("经理A"));
        employeeList.add(new Engineer("Java工程师 - 赵云"));
        employeeList.add(new Engineer("Java工程师 - 后裔"));
        employeeList.add(new Manager("经理B"));
        employeeList.add(new Engineer("Java工程师 - 妲己"));
        employeeList.add(new Engineer("Java工程师 - 露娜"));
    }

    //遍历
    public void showReport(IVisitor visitor){
        for (Employee employee : this.employeeList) {
            employee.accept(visitor);
        }
    }
}

(6)测试

    public static void main(String[] args) {
        BusinessReport businessReport = new BusinessReport();

        System.out.println("=========CEO 看报表==============");
        businessReport.showReport(new CEOVisitor());

        System.out.println("=========CTO 看报表==============");
        businessReport.showReport(new CTOVisitor());
    }

在这里插入图片描述

– 求知若饥,虚心若愚。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值