一、访问者模式
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());
}
– 求知若饥,虚心若愚。