Java设计模式之访问者模式

概述

访问者模式是一种将数据操作与数据结构分离的模式
是23中设计模式中最复杂的一个,使用频率也不是很高,但一旦需要它的时候将变得非常有用.

软件系统中拥有一个由许多对象构成的,比较稳定的对象结构.这些对象的类都拥有一个accept方法用来接收访问者对象的访问.
访问者是一个接口,拥有visit方法,这个方法对访问到的对象结构中不同类型的元素做出不同的处理.

在对象结构的一次访问过程中,我们遍历整个对象结构,对每个元素都实施accept方法,在每个元素的accept方法中会调用访问者的visit方法,使得访问者得以处理对象结构的每一个元素.

定义

封装一些作用于某种数据结构中的各个元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作.

使用场景

  • 对象结构比较稳定,但经常需要在此对象结构上定义新的操作.
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作污染这些对象本身的时候

UML类图

访问者模式UML

其中涉及到的角色介绍:

  • Visitor : 接口或抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的了类族要稳定.
  • ConcreteVisitor : 具体的访问者,它需要给出对每一个元素类访问时所产生的具体行为
  • Element : 元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问.
  • ElementAA,ElementB : 具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法.
  • ObjectStructure : 定义当中所提到的数据结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问.

实例

访问者模式是一个结构,概念都较为复杂的设计模式,接下来我们用一个简单的实例来看一下其使用,

如下实例模拟了公司年终业绩考核问题,为了简单说明问题,把员工简单分为工程师和经理,评定员工分别为CEOCTO,
假定CTO之关注工程师代码质量和经理的新产品数量,而CEO之关注工程师的KPI和经理的KPI以及新产品数量

  • 首先定以员工类接口
public abstract class Staff {

  public String name;
  public int kpi;

  public Staff(String _name) {
    name = _name;
    kpi = new Random().nextInt(10);
  }

  public abstract void accept(Visitor _visitor);
}
  • 定义工程师和经理,继承与员工类
public class Manager extends Staff {
  public Manager(String _name) {
    super(_name);
  }

  @Override public void accept(Visitor _visitor) {
    _visitor.visit(this);
  }

  public int getProducts() {
    return new Random().nextInt(10);
  }
}


public class Engineer extends Staff {
  public Engineer(String _name) {
    super(_name);
  }

  @Override public void accept(Visitor _visitor) {
      _visitor.visit(this);
  }

  public int getCoceLines(){
    return new Random().nextInt(10*10000);
  }
}
  • 定义Visitor接口

    public interface Visitor {
    
    void visit(Engineer _engineer);
    
    void visit(Manager _manager);
    }
  • 定义具体的Visitor,实现Visitor接口,

    public class CEOVisitor implements Visitor {
    private static final String TAG = "CEOVisitor";
    
    @Override public void visit(Engineer _engineer) {
    Log.d(TAG, "visit: -->" + "工程师 : " + _engineer.name + ",kpi : " + _engineer.kpi);
    }
    
    @Override public void visit(Manager _manager) {
    Log.d(TAG, "visit: -->"
        + "经理 : "
        + _manager.name
        + ",kpi : "
        + _manager.kpi
        + ",新产品数 : "
        + _manager.getProducts());
    }
    }
    //....
  • 客户端调用

    为了方便,这里加了一个报表类

    public class BusinessReport {
    
    List<Staff> mStaffs = new LinkedList<>();
    
    public BusinessReport() {
     mStaffs.add(new Manager("王经理"));
     mStaffs.add(new Engineer("工程师小刘"));
    //...
    }
    
    public void showReport(Visitor _visitor) {
     for (Staff staff : mStaffs) {
       staff.accept(_visitor);
     }
    }
    }

    真正调用的时候

    BusinessReport businessReport = new BusinessReport();
    
    Log.d(TAG, "给CEO看报表 ");
    businessReport.showReport(new CEOVisitor());
    
    Log.d(TAG, "给CTO看报表");
    businessReport.showReport(new CTOVisitor());

优缺点

优点:

  • 各个角色职责分离,符合单一职责原则.
  • 优秀的扩展性
  • 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
  • 灵活性高

缺点

  • 具体元素对访问者公布细节,违反了迪米特原则
  • 具体元素变更时导致修改成本大
  • 违反了依赖倒置原则,为了达到区别对待,而依赖了具体类,而不是抽象.

    实例代码

    DesignPatterns

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值