先看字面定义
偷图
偷代码
- 核心在于accept时,每个元素的实现都在visit(this)
// 访问者接口
interface Visitor {
void visit(TextElement element);
void visit(ImageElement element);
void visit(TableElement element);
}
// 具体访问者:统计字符数
class CharacterCounter implements Visitor {
int count = 0;
@Override
public void visit(TextElement element) {
count += element.getText().length();
}
// 图片和表格访问不做处理
@Override
public void visit(ImageElement element) {}
@Override
public void visit(TableElement element) {}
// 获取统计结果
public int getCount() { return count; }
}
// 具体访问者:统计图片数量
class ImageCounter implements Visitor {
int count = 0;
@Override
public void visit(TextElement element) {}
@Override
public void visit(ImageElement element) { count++; }
@Override
public void visit(TableElement element) {}
// 获取统计结果
public int getCount() { return count; }
}
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素:文本
class TextElement implements Element {
private String text;
public TextElement(String text) { this.text = text; }
public String getText() { return text; }
@Override
public void accept(Visitor visitor) { visitor.visit(this); }
}
// 其他具体元素:图片、表格的定义省略...
// 对象结构:文档
class Document {
private List<Element> elements;
public Document() { elements = new ArrayList<>(); }
public void add(Element element) { elements.add(element); }
public void print(Visitor visitor) {
for (Element e : elements) {
e.accept(visitor);
}
}
}
// 示例使用
public class VisitorPatternDemo {
public static void main(String[] args) {
Document doc = new Document();
doc.add(new TextElement("Hello, World!"));
doc.add(new ImageElement());
doc.add(new TableElement());
Visitor counter1 = new CharacterCounter();
doc.print(counter1);
System.out.println("Total characters: " + counter1.getCount());
Visitor counter2 = new ImageCounter();
doc.print(counter2);
System.out.println("Total images: " + counter2.getCount());
}
}
省流
- 我受够了繁文缛节:就是将【元素】聚合后,如何便捷的用不同【算法】处理每个元素
- 访问者会让人有歧义以为是只读,实际不是,经典翻译锅
为此访问者模式做了什么?
- 外部发起visit,实际控制权转交给遍历的每个【元素】,虽然元素顶层接口的accept的参数也是vistor的顶层接口而不是实现类,乍一看是没法通过编译的,因为方法不确定。
-
但实际上巧妙的用了java多态,实例化元素的函数在运行时是可以确定的,入参是明确的vistor实现类。
-
一般遍历需要进行的类型或其他什么东西的判断,被转移到了多态寻找实现类函数的时机上
-
暴论
为什么我们不需要访问者模式
-
每种【算法】都要针对每种【元素】进行对应的策略方法重写,tmd笛卡尔积产生了哥哥。
- 看起来是实际也是巧妙的多态应用,但是并不实用。
- 确实减少了算法代码中需要的显式判断类型或其他什么东西的逻辑,但也变相增加了限制:你的不同元素区别必须使用多态类来表达,如果是一般业务中的一些联合判断/type什么的就不行了。
-
在利用多态实现的过程中,产生了大量的虚函数(接口方法的实现),JVM无法对其进行内联优化,导致运行时决定具体虚函数调用性能堪忧。
- 而访问者的背景有“每个”这一遍历特征,积少成多,性能损耗大
-
访问者的设计多态很美丽,但是并不易读(防谁呢,这么绕),也不易维护,属于感动自己。
- 在管理过程中,可视性大于一切,大可以管理手段维护好每次变动和增量。
我们该如何实现类似的效果
-
斗胆
- 对于元素的聚合依旧是必要的
- 不同算法各写各的返璞归真
- 提供一个命中分析器,分析统一管理元素对于算法是否该命中
-
毕竟没场景,8886