解法就是:将行为/业务抽象成单独的类,函数传入不同的访问者,根据不同的访问者入参执行对应操作,访问者从主动变成被动,以此规避了编译失败问题。接着代码实现一波:
访问角色
,引擎和车身,传入不同的访问者,执行不同的操作:
// 抽象访问角色类 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函数重载,调用哪个重载函数,取决于入参的声明类型(编译器时类型),所以它只支持单分派。
上面例子就是使用观察模式间接实现双分派,当然也可以使用其他方式实现,如前面学的工厂方法模式~
以上内容就是本节的全部内容,谢谢~