把书读薄 | 《设计模式之美》设计模式与范式(行为型-访问者模式)(上)

简介: 本文对应设计模式与范式:行为型(68-69),访问者模式 (Visitor Pattern),用于 解耦对象结构与对象操作。其难点在于代码实现比较复杂,因为大部分面向对象编程语言中是 静态绑定 的。调用类的哪个重载函数,是在 编译期由函数声明类型决定,而非 运行时根据参数实际类型决定 的。代码实现难理解,在项目中应用此模式可能导致可读性较差,没有特别必要的情况,建议不要使用这种模式~

0x1、定义


原始定义


允许在 运行时 将一个或多个操作应用于一组对象,将操作与对象结构分离。


简单点说


一组对象,对象结构可以各不相同,但必须以某个或一组操作作为连接的中心点,即:


以行为(某个操作) 作为扩展对象的出发点,在不改变已有类的功能前提下进行批量扩展。


0x2、写个简单例子


以汽车结构为例,里面包含了引擎,车身等,不同角色的人可以对这些结构进行不同的访问,如:


  • 司机 → 查看
  • 洗车佬 → 清洁
  • 维修佬 → 检查维修


不用访问者,代码实现一波:


public abstract class AbstractVisitor {
    protected String name;
    public AbstractVisitor(String name) { this.name = name; }
    abstract void visitorEngine();
    abstract void visitorBody();
}
public class DriverVisitor extends AbstractVisitor {
    public DriverVisitor(String name) { super(name); }
    @Override void visitorEngine() { System.out.println(name + " → 查看引擎"); }
    @Override void visitorBody() { System.out.println(name + " → 查看车身"); }
}
public class CleanerVisitor extends AbstractVisitor {
    public CleanerVisitor(String name) { super(name); }
    @Override void visitorEngine() { System.out.println(name + " → 清洗引擎"); }
    @Override void visitorBody() { System.out.println(name + " → 清洗车身"); }
}
public class RepairVisitor extends AbstractVisitor {
    public RepairVisitor(String name) { super(name); }
    @Override void visitorEngine() { System.out.println(name + " → 修理引擎"); }
    @Override void visitorBody() { System.out.println(name + " → 修理车身"); }
}
// 测试用例
public class Test {
    public static void main(String[] args) {
        List<AbstractVisitor> visitors = new ArrayList<>();
        visitors.add(new DriverVisitor("杰哥"));
        visitors.add(new CleanerVisitor("老王"));
        visitors.add(new RepairVisitor("大锤"));
        for(AbstractVisitor visitor: visitors) {
            visitor.visitorEngine();
            visitor.visitorBody();
        }
    }
}


代码运行结果如下


网络异常,图片无法展示
|


可以,但存在问


  • 想添加功能,如支持访问轮胎,那么所有类都要改动,违反了开闭原则;
  • 访问逻辑都耦合到访问者类中,导致它们的职责不够单一,成了大杂化;


拆分解耦,把业务操作与具体的数据结构解耦,设计成独立的类,用访问者模式对象上述代码重构:


// 抽象访问者
public abstract class AbstractVisitor {
    protected String name;
    public AbstractVisitor(String name) { this.name = name; }
}
// 具体访问者
public class DriverVisitor extends AbstractVisitor {
    public DriverVisitor(String name) { super(name); }
}
public class CleanerVisitor extends AbstractVisitor {
    public CleanerVisitor(String name) { super(name); }
}
public class RepairVisitor extends AbstractVisitor {
    public RepairVisitor(String name) { super(name); }
}
// 对象结构
public class Car {
    public void visitorEngine(DriverVisitor visitor) {
        System.out.println(visitor.name + " → 查看引擎");
    }
    public void visitorEngine(CleanerVisitor visitor) {
        System.out.println(visitor.name + " → 清洗引擎");
    }
    public void visitorEngine(RepairVisitor visitor) {
        System.out.println(visitor.name + " → 修理引擎");
    }
    public void visitorBody(DriverVisitor visitor) {
        System.out.println(visitor.name + " → 查看车身");
    }
    public void visitorBody(CleanerVisitor visitor) {
        System.out.println(visitor.name + " → 清洗车身");
    }
    public void visitorBody(RepairVisitor visitor) {
        System.out.println(visitor.name + " → 修理车身");
    }
}
// 测试用例
public class Test {
    public static void main(String[] args) {
        List<AbstractVisitor> visitors = new ArrayList<>();
        visitors.add(new DriverVisitor("杰哥"));
        visitors.add(new CleanerVisitor("老王"));
        visitors.add(new RepairVisitor("大锤"));
        Car car = new Car();
        for(AbstractVisitor visitor: visitors) {
            // 此处编译不通过
            car.visitorEngine(visitor);
            car.visitorBody(visitor);
        }
    }
}


上述代码理论上是可行,利用函数重载,根据参数类型调用对应方法,实际上却是编译都过不了!


原因是:


Java中的 函数重载 是一种 静态绑定,编译时并不能获取对象的 实际类型,而是根据 声明类型 执行声明类型对应的方法。


相关文章
|
4天前
|
设计模式 存储 SQL
第四篇 行为型设计模式 - 灵活定义对象间交互
第四篇 行为型设计模式 - 灵活定义对象间交互
|
5天前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
9 2
|
5天前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
5天前
|
设计模式 JavaScript Java
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
[设计模式Java实现附plantuml源码~行为型] 对象状态及其转换——状态模式
|
5天前
|
设计模式 Go
[设计模式 Go实现] 行为型~解释器模式
[设计模式 Go实现] 行为型~解释器模式
|
5天前
|
设计模式 Go
[设计模式 Go实现] 行为型~迭代器模式
[设计模式 Go实现] 行为型~迭代器模式
|
5天前
|
设计模式 存储 Go
[设计模式 Go实现] 行为型~备忘录模式
[设计模式 Go实现] 行为型~备忘录模式
|
5天前
|
设计模式 Go
[设计模式 Go实现] 行为型~中介者模式
[设计模式 Go实现] 行为型~中介者模式
|
5天前
|
设计模式 Go
[设计模式 Go实现] 行为型~观察者模式
[设计模式 Go实现] 行为型~观察者模式
|
5天前
|
设计模式 Go
[设计模式 Go实现] 行为型~状态模式
[设计模式 Go实现] 行为型~状态模式