【JAVA】设计模式-暴论:我们不需要访问者模式

76 阅读3分钟

先看字面定义

偷图 image.png

偷代码

  • 核心在于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