目录
我们知道,如果一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖其具体类型的操作,但又避免让这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类,那么可以使用访问者设计模式;它使得我们可以将相关的访问操作集中起来定义在访问者类中,将对象本身与对象的访问操作分离。
在cola框架中,针对状态机(StateMachine)和状态(State)就应用了访问者模式,下面进行具体分析;
附:
cola状态机源码解析见上篇文章:cola架构:有限状态机(FSM)源码分析
访问者(Visitor)模式:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式。
访问者模式包含的类/接口如下:
抽象访问者(Visitor):定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
具体访问者(ConcreteVisitor):实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
抽象元素(Element):声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
具体元素(ConcreteElement):实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作
对象结构(Object Structure):是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。
相关类图及引用结构如下:

cola被访问者类图如下,包含被访问者State和StateMachine:

Visitable接口定义了accept接口,接受Visitor参数对象:
- public interface Visitable {
- String accept(final Visitor visitor);
- }
StateImpl实现accept接口如下:
- public class StateImpl
implements State { -
- @Override
- public String accept(Visitor visitor) {
- String entry = visitor.visitOnEntry(this);
- String exit = visitor.visitOnExit(this);
- return entry + exit;
- }
- }
StateMachineImpl实现accept接口如下:
- public class StateMachineImpl
implements StateMachine { -
- @Override
- public String accept(Visitor visitor) {
- StringBuilder sb = new StringBuilder();
- sb.append(visitor.visitOnEntry(this));
- for (State state : stateMap.values()) {
- sb.append(state.accept(visitor));
- }
- sb.append(visitor.visitOnExit(this));
- return sb.toString();
- }
- }
实现逻辑里,通过简单调用访问者类的visit方法完成对被访问者的访问。
cola访问者包含PlantUMLVisitor和SysOutVisitor,类图如下:

Visitor接口约定了分别针对State和StateMachine的visit接口方法:
- /**
- * Visitor
- *
- * @author Frank Zhang
- * @date 2020-02-08 8:41 PM
- */
- public interface Visitor {
-
- char LF = '\n';
-
- /**
- * @param visitable the element to be visited.
- * @return
- */
- String visitOnEntry(StateMachine, ?, ?> visitable);
-
- /**
- * @param visitable the element to be visited.
- * @return
- */
- String visitOnExit(StateMachine, ?, ?> visitable);
-
- /**
- * @param visitable the element to be visited.
- * @return
- */
- String visitOnEntry(State, ?, ?> visitable);
-
- /**
- * @param visitable the element to be visited.
- * @return
- */
- String visitOnExit(State, ?, ?> visitable);
- }
- /**
- * PlantUMLVisitor
- *
- * @author Frank Zhang
- * @date 2020-02-09 7:47 PM
- */
- public class PlantUMLVisitor implements Visitor {
-
- /**
- * Since the state machine is stateless, there is no initial state.
- *
- * You have to add "[*] -> initialState" to mark it as a state machine diagram.
- * otherwise it will be recognized as a sequence diagram.
- *
- * @param visitable the element to be visited.
- * @return
- */
- @Override
- public String visitOnEntry(StateMachine, ?, ?> visitable) {
- return "@startuml" + LF;
- }
-
- @Override
- public String visitOnExit(StateMachine, ?, ?> visitable) {
- return "@enduml";
- }
-
- @Override
- public String visitOnEntry(State, ?, ?> state) {
- StringBuilder sb = new StringBuilder();
- for(Transition transition: state.getAllTransitions()){
- sb.append(transition.getSource().getId())
- .append(" --> ")
- .append(transition.getTarget().getId())
- .append(" : ")
- .append(transition.getEvent())
- .append(LF);
- }
- return sb.toString();
- }
-
- @Override
- public String visitOnExit(State, ?, ?> state) {
- return "";
- }
- }
- /**
- * SysOutVisitor
- *
- * @author Frank Zhang
- * @date 2020-02-08 8:48 PM
- */
- public class SysOutVisitor implements Visitor {
-
- @Override
- public String visitOnEntry(StateMachine, ?, ?> stateMachine) {
- String entry = "-----StateMachine:"+stateMachine.getMachineId()+"-------";
- System.out.println(entry);
- return entry;
- }
-
- @Override
- public String visitOnExit(StateMachine, ?, ?> stateMachine) {
- String exit = "------------------------";
- System.out.println(exit);
- return exit;
- }
-
- @Override
- public String visitOnEntry(State, ?, ?> state) {
- StringBuilder sb = new StringBuilder();
- String stateStr = "State:"+state.getId();
- sb.append(stateStr).append(LF);
- System.out.println(stateStr);
- for(Transition transition: state.getAllTransitions()){
- String transitionStr = " Transition:"+transition;
- sb.append(transitionStr).append(LF);
- System.out.println(transitionStr);
- }
- return sb.toString();
- }
-
- @Override
- public String visitOnExit(State, ?, ?> visitable) {
- return "";
- }
- }
至此,应用访问者模式,实现了将被访问类和访问操作进行逻辑隔离、解耦。