这是我参与8月更文挑战的第6天,活动详情查看: 8月更文挑战java
😁 周三已到,周末还会远吗?继续啃设计模式之美,本文对应设计模式与范式:行为型(68-69),访问者模式 (Visitor Pattern),用于 解耦对象结构与对象操做
。编程
其难点在于代码实现比较复杂,由于大部分面向对象编程语言中是 静态绑定 的。调用类的哪一个重载函数,是在 编译期由函数声明类型决定,而非 运行时根据参数实际类型决定 的。设计模式
代码实现难理解,在项目中应用此模式可能致使可读性较差,没有特别必要的状况,建议不要使用这种模式~markdown
固然,了解下也无妨,万一接盘时真遇到了别人用这种模式,也不至于一脸懵逼~数据结构
Tips:二手知识加工不免有所纰漏,感兴趣有时间的可自行查阅原文,谢谢。app
原始定义编程语言
容许在 运行时 将一个或多个操做应用于一组对象,将操做与对象结构分离。ide
简单点说函数
一组对象,对象结构能够各不相同,但必须以某个或一组操做做为链接的中心点,即:post
以行为(某个操做) 做为扩展对象的出发点,在不改变已有类的功能前提下进行批量扩展。
以汽车结构为例,里面包含了引擎,车身等,不一样角色的人能够对这些结构进行不一样的访问,如:
不用访问者,代码实现一波:
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中的
函数重载
是一种静态绑定
,编译时并不能获取对象的实际类型
,而是根据声明类型
执行声明类型对应的方法。
解法就是:将行为/业务抽象成单独的类,函数传入不一样的访问者,根据不一样的访问者入参执行对应操做,访问者从主动变成被动,以此规避了编译失败问题。接着代码实现一波:
访问角色
,引擎和车身,传入不一样的访问者,执行不一样的操做:
// 抽象访问角色类
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类图、组成角色、使用场景及优缺点:
使用场景
优势:
缺点
哪一个对象的方法
,根据 对象的运行时类型
决定;对象的哪一个方法
,根据 方法参数的编译时类型
决定;哪一个对象的方法
,根据 对象的运行时类型
决定;对象的哪一个方法
,根据 方法参数的运行时类型
决定;Java函数重载,调用哪一个重载函数,取决于入参的声明类型(编译器时类型),因此它只支持单分派。
上面例子就是使用观察模式间接实现双分派,固然也可使用其余方式实现,如前面学的工厂方法模式~
以上内容就是本节的所有内容,谢谢~