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

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

解法就是:将行为/业务抽象成单独的类,函数传入不同的访问者,根据不同的访问者入参执行对应操作,访问者从主动变成被动,以此规避了编译失败问题。接着代码实现一波:


访问角色,引擎和车身,传入不同的访问者,执行不同的操作:


// 抽象访问角色类
public interface Visit {
    void visit(DriverVisitor visitor);
    void visit(CleanerVisitor visitor);
    void visit(RepairVisitor visitor);
}
// 访问角色具体实现类
public class Engine implements Visit {
    @Override public void visit(DriverVisitor visitor) {
        System.out.println(visitor.name + "→ 查看引擎");
    }
    @Override public void visit(CleanerVisitor visitor) {
        System.out.println(visitor.name + "→ 清洗引擎");
    }
    @Override public void visit(RepairVisitor visitor) {
        System.out.println(visitor.name + "→ 修理引擎");
    }
}
public class Body implements Visit {
    @Override public void visit(DriverVisitor visitor) {
        System.out.println(visitor.name + "→ 查看车身");
    }
    @Override public void visit(CleanerVisitor visitor) {
        System.out.println(visitor.name + "→ 清洗车身");
    }
    @Override public void visit(RepairVisitor visitor) {
        System.out.println(visitor.name + "→ 修理车身");
    }
}


再接着是 访问者,定义所有 访问角色 的访问操作:


// 抽象访问者类
public abstract class AbstractVisitor {
    protected String name;
    public AbstractVisitor(String name) { this.name = name; }
    abstract void visitorEngine(Engine engine);
    abstract void visitorBody(Body body);
}
// 具体访问者类
public class DriverVisitor extends AbstractVisitor {
    public DriverVisitor(String name) { super(name); }
    @Override void visitorEngine(Engine engine) { engine.visit(this); }
    @Override void visitorBody(Body body) { body.visit(this); }
}
public class CleanerVisitor extends AbstractVisitor {
    public CleanerVisitor(String name) { super(name); }
    @Override void visitorEngine(Engine engine) { engine.visit(this); }
    @Override void visitorBody(Body body) { body.visit(this); }
}
public class RepairVisitor extends AbstractVisitor {
    public RepairVisitor(String name) { super(name); }
    @Override void visitorEngine(Engine engine) { engine.visit(this); }
    @Override void visitorBody(Body body) { body.visit(this); }
}


接着上测试用例:


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("大锤"));
        // 实例化访问角色
        Engine engine = new Engine();
        Body body = new Body();
        for(AbstractVisitor visitor: visitors) {
            visitor.visitorEngine(engine);
            visitor.visitorBody(body);
        }
    }
}


代码运行结果同上,可以,但还存在添加新业务,每个访问者都要改动的问题,还要再改改~


// 抽象访问者类
public class RepairVisitor extends AbstractVisitor {
    public RepairVisitor(String name) { super(name); }
    @Override void accept(Visit visit) { visit.visit(this); }
}
// 具体访问者类
public class DriverVisitor extends AbstractVisitor {
    public DriverVisitor(String name) { super(name); }
    @Override void accept(Visit visit) { visit.visit(this); }
}
public class CleanerVisitor extends AbstractVisitor {
    public CleanerVisitor(String name) { super(name); }
    @Override void accept(Visit visit) { visit.visit(this); }
}
public class RepairVisitor extends AbstractVisitor {
    public RepairVisitor(String name) { super(name); }
    @Override void accept(Visit visit) { visit.visit(this); }
}
// 测试用例
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("大锤"));
        // 实例化访问角色
        Engine engine = new Engine();
        Body body = new Body();
        for(AbstractVisitor visitor: visitors) {
            visitor.accept(engine);
            visitor.accept(body);
        }
    }
}


改完后,此时我们要增加一个访问轮胎的业务,只需让其实现Visit接口,实例化后直接accept()即可,不用改动访问者角色代码,妙啊!!!


顺带带出UML类图、组成角色、使用场景及优缺点:


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


  • Visitor (抽象访问者) → 定义声明所有访问角色的访问操作;
  • ConcreteVisitor (具体访问者) → 实现抽象访问者中实现的所有访问方法;
  • Element (抽象访问角色) → 定义接收具体访问者的方法;
  • ConcreteElement (具体访问角色) → 实现抽象访问角色,并实现具体操作;
  • ObjectStructure (对象结构) → 备选,使访问者能够访问到对应的元素;


使用场景


  • 对象数据结构相对稳定,而操作却经常变化;
  • 需将数据结构与不常用操作进行分离;
  • 需在运行时动态决定使用哪些对象和方法;


优点


  • 满足开闭原则和单一职责原则;
  • 可扩展性,增加新的访问操作及访问者非常方便;


缺点


  • 不适用于结构经常变化的场景;
  • 具体访问角色变更时需修改代码,易引入问题;


0x3、加餐:单分派和双分派概念


① 单分派


  • 执行 哪个对象的方法,根据 对象的运行时类型 决定;


  • 执行 对象的哪个方法,根据 方法参数的编译时类型 决定;


② 双分派

  • 执行 哪个对象的方法,根据 对象的运行时类型 决定;


  • 执行 对象的哪个方法,根据 方法参数的运行时类型 决定;


Java函数重载,调用哪个重载函数,取决于入参的声明类型(编译器时类型),所以它只支持单分派。


上面例子就是使用观察模式间接实现双分派,当然也可以使用其他方式实现,如前面学的工厂方法模式~


以上内容就是本节的全部内容,谢谢~


相关文章
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
30 3
|
6月前
|
设计模式 算法 Java
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
【设计模式】JAVA Design Patterns——Acyclic Visitor(非循环访问者模式)
|
2月前
|
设计模式 缓存 算法
Java设计模式-访问者模式(22)
Java设计模式-访问者模式(22)
|
5月前
|
设计模式 算法
行为型设计模式之模板模式
行为型设计模式之模板模式
|
5月前
|
设计模式 存储
行为型设计模式之观察者模式
行为型设计模式之观察者模式
|
5月前
|
设计模式 算法
行为型设计模式
行为型设计模式
|
6月前
|
设计模式 安全 Java
【设计模式】字节三面:请举例阐释访问者模式
【设计模式】字节三面:请举例阐释访问者模式
42 2
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~状态模式
[设计模式 Go实现] 行为型~状态模式
|
6月前
|
设计模式 Go
[设计模式 Go实现] 行为型~迭代器模式
[设计模式 Go实现] 行为型~迭代器模式
|
6月前
|
设计模式 存储 Go
[设计模式 Go实现] 行为型~备忘录模式
[设计模式 Go实现] 行为型~备忘录模式