访问者设计模式
二十三种经典设计模式之一 适用于,操作与结构分离,结构相对固定,操作变化比较频繁场景, 访问者模式能把操作从结构中分离出来,并根据需要增加新的操作,而不用修改原来的程序代码与数据结构,提高了程序的扩展性。 主要角色有: 对象结构、抽象元素、具体元素、抽象访问者、具体访问者
对象结构:
包含所有抽象元素(数据结构),通常是一个Collection 或者 Map ,接收一个或多个访问者(处理方法),遍历所有抽象元素,应用抽象元素到当前访问者
抽象元素、具体元素
主要是接收一个访问者
void accept(Vistor vistor);
void accept(Vistor vistor){
vistor.visitXXX1(this);
}
抽象访问者、具体访问者
主要是对不同的元素执行不同的操作 visitXXX
visitXXX1(T1 t);
visitXXX2(T2 t);
举例说明
这个场景下,包括以下要素:动物园,动物园中的各个场馆,不同类型的游客,不同类型的游客票价不同。 动物园就相当于一个对象结构,该结构包含具体的元素-各个场馆,每个场馆(元素)都有接待游客(visitor)的方法(accept)。 这些被处理的数据元素相对稳定(动物园中的场馆一般比较稳定)而访问方式多种多样(比如学生散客,学生团体,普通游客,团体游客等不同的访问方式)的数据结构,如果用“访问者模式”来处理比较方便。
UML
代码实现
抽象类分析转为代码
对象结构
public class Zoo {
// save all scenery spot
private List<ScenerySpot> scenerySpotList = new ArrayList<>();
// accept visitor
public void accept(Visitor visitor) {
for (ScenerySpot scenerySpot : scenerySpotList) {
scenerySpot.accept(visitor);
}
}
public void addScenerySpot(ScenerySpot scenerySpot) {
scenerySpotList.add(scenerySpot);
}
public void removeScenerySpot(ScenerySpot scenerySpot) {
scenerySpotList.remove(scenerySpot);
}
}
抽象元素和具体元素
抽象场景管
public interface ScenerySpot {
void accept(Visitor visitor);
Integer ticketRate();
}
具体场景管-老虎场景管
public class TriggerScenerySpot implements ScenerySpot{
public void accept(Visitor visitor) {
visitor.visitorTriggerScenerySpot(this);
}
public Integer ticketRate() {
return 100;
}
}
具体场景管-孔雀场景管
public class PeafowlScenerySpot implements ScenerySpot{
public void accept(Visitor visitor) {
visitor.visitorPeafowlScenerySpot(this);
}
public Integer ticketRate() {
return 80;
}
}
抽象访问者和具体访问者
抽象访问者
public interface Visitor {
void visitorTriggerScenerySpot(TriggerScenerySpot triggerScenerySpot);
void visitorPeafowlScenerySpot(PeafowlScenerySpot peafowlScenerySpot);
}
具体访问者-学生
public class StudentVisitor implements Visitor {
public void visitorTriggerScenerySpot(TriggerScenerySpot triggerScenerySpot) {
System.out.println("student visitor trigger scenery spot, ticket is "
+ (int) (triggerScenerySpot.ticketRate() * 0.5));
}
public void visitorPeafowlScenerySpot(PeafowlScenerySpot peafowlScenerySpot) {
System.out.println("student visitor peafowl scenery spot, ticket is "
+ (int) (peafowlScenerySpot.ticketRate() * 0.5));
}
}
具体访问者-普通
public class CommonVisitor implements Visitor {
public void visitorTriggerScenerySpot(TriggerScenerySpot triggerScenerySpot) {
System.out.println("common visitor trigger scenery spot, ticket is "
+ triggerScenerySpot.ticketRate());
}
public void visitorPeafowlScenerySpot(PeafowlScenerySpot peafowlScenerySpot) {
System.out.println("common visitor peafowl scenery spot, ticket is "
+ peafowlScenerySpot.ticketRate());
}
}
使用
public class Client {
public static void main(String[] args) {
Zoo zoo = new Zoo();
zoo.addScenerySpot(new PeafowlScenerySpot());
zoo.addScenerySpot(new TriggerScenerySpot());
StudentVisitor studentVisitor = new StudentVisitor();
zoo.accept(studentVisitor);
CommonVisitor commonVisitor = new CommonVisitor();
zoo.accept(commonVisitor);
}
}
输出
student visitor peafowl scenery spot, ticket is 40
student visitor trigger scenery spot, ticket is 50
common visitor peafowl scenery spot, ticket is 80
common visitor trigger scenery spot, ticket is 100
Antlr Visitor 模式
OBMySQL.g4 selectStatement 主要内容如下
select_stmt
: select_no_parens
| select_with_parens
| select_into
;
select_into
: select_no_parens into_clause
;
select_with_parens
: LeftParen (select_no_parens|select_with_parens) RightParen
;
select_no_parens
: select_clause (FOR UPDATE opt_for_update_wait)?
| select_clause_set (FOR UPDATE opt_for_update_wait)?
| select_clause_set_with_order_and_limit (FOR UPDATE opt_for_update_wait)?
;
no_table_select
: select_with_opt_hint query_expression_option_list? select_expr_list into_opt
| select_with_opt_hint query_expression_option_list? select_expr_list into_opt FROM DUAL (WHERE opt_hint_value expr)? (GROUP BY groupby_clause)? (HAVING expr)? (WINDOW named_windows)?
;
select_clause
: no_table_select
| no_table_select_with_order_and_limit
| simple_select
| simple_select_with_order_and_limit
| select_with_parens_with_order_and_limit
;
select_clause_set_with_order_and_limit
: select_clause_set order_by
| select_clause_set order_by? limit_clause
;
select_clause_set
: select_clause_set set_type select_clause_set_right
| select_clause_set order_by set_type select_clause_set_right
| select_clause_set order_by? limit_clause set_type select_clause_set_right
| select_clause_set_left set_type select_clause_set_right
;
select_clause_set_right
: no_table_select
| simple_select
| select_with_parens
;
select_clause_set_left
: no_table_select_with_order_and_limit
| simple_select_with_order_and_limit
| select_clause_set_right
;
no_table_select_with_order_and_limit
: no_table_select order_by
| no_table_select order_by? limit_clause
;
simple_select_with_order_and_limit
: simple_select order_by
| simple_select order_by? limit_clause
;
select_with_parens_with_order_and_limit
: select_with_parens order_by
| select_with_parens order_by? limit_clause
;
select_with_opt_hint
: SELECT
| SELECT_HINT_BEGIN hint_list_with_end
;
update_with_opt_hint
: UPDATE
| UPDATE_HINT_BEGIN hint_list_with_end
;
delete_with_opt_hint
: DELETE
| DELETE_HINT_BEGIN hint_list_with_end
;
simple_select
: select_with_opt_hint query_expression_option_list? select_expr_list into_opt FROM from_list where_clause? (GROUP BY groupby_clause)? having_clause? (WINDOW named_windows)?
;
where_clause
: WHERE opt_hint_value expr
;
having_clause
: HAVING expr
;
set_type_union
: UNION
;
set_type_other
: INTERSECT
| EXCEPT
| MINUS
;
set_type
: set_type_union set_expression_option
| set_type_other
;
set_expression_option
: (ALL | DISTINCT | UNIQUE)?
;
opt_hint_value
: HINT_VALUE?
;
limit_clause
: LIMIT limit_expr ((OFFSET limit_expr)?|Comma limit_expr)
;
通过 antlr 解析g4自动生成代码如下
OBParser 结构
每一个规则生成一个 ParserRuleContext� 子类,如 Select_stmtContext�,Select_no_parensContext�、Select_with_parensContext�� ,ParserRuleContext 实现 ParseTree�,** **
ParseTree� 如下,accept 接收一个 visitor ParseTree 对应 Visitor 模式的抽象元素
public interface ParseTree extends SyntaxTree {
<T> T accept(ParseTreeVisitor<? extends T> visitor);
}
AbstractParseTreeVisitor 实现如下
public abstract class AbstractParseTreeVisitor<T> implements ParseTreeVisitor<T> {
@Override
public T visit(ParseTree tree) {
return tree.accept(this);
}
}
Select_stmtContext� 重写 accept, 调用规则visitSelect_stmt� 对应的visit 方法:visitSelect_stmt�
public static class Select_stmtContext extends ParserRuleContext {
public <T> T accept(ParseTreeVisitor<? extends T> visitor) {
return visitor instanceof OBParserVisitor ? ((OBParserVisitor)visitor).visitSelect_stmt(this) : visitor.visitChildren(this);
}
}
}
MysqlSelectParser� 实现如下
每个自定义 XXXParser 相当一个visitor
public class MysqlSelectParser extends OBParserBaseVisitor<Select>{
@Override
public Select visitSelect_stmt(Select_stmtContext ctx) {
SelectBody selectBody = new MysqlSelectBodyFactory(ctx.select_with_parens()).generate();
return new Select(ctx, selectBody);
}
}
MysqlSelectBodyParser� 实现如下
public class MysqlSelectBodyParser extends OBParserBaseVisitor<SelectBody>{
@Override
public SelectBody visitSelect_with_parens(Select_with_parensContext ctx) {
SelectBody selectbody = new SelectBody(ctx, visit(ctx.select_with_parens()));
return selectbody;
}
}
�
UML 如下
访问不同的规则只需要添加 MysqlSelectParser 不同的 visitor ,继承 OBParserBaseVisitor, 重写对应的visitXXX() 方法。
执行基本流程
/ Step1. 新建CharStream,从标准输入读取数据
CharStream input = CharStreams.fromString("SELECT * FROM AUDIT_EVENT WHERE ID=1");
// Step2. 新建词法分析器,处理输入的charStream[!]
CreateLexer lexer = new CreateLexer(input);
// Step3. 新建词法符号的缓冲区,用于存储词法分析器将生成的词法符号
CommonTokenStream tokens = new CommonTokenStream(lexer);
// Step4. 新建语法分析器,处理词法符号缓冲区的内容[!]
CreateParser parser = new CreateParser(tokens);
// Step5. 针对stat规则,开始语法分析.
ParseTree tree = parser.stat();
StmtContext stmt = parser.stmt();
MysqlSelectParser visitor = new MysqlSelectParser();
visitor.visitor(stmt.select_stmt());
后面执行流程如下
MysqlSelectParser.visit(Select_stmtContext);
--> AbstractParseTreeVisitor.visit(ParseTree);
--> Select_stmtContext.accept(MysqlSelectParser);
--> MysqlSelectParser.visitSelect_stmt();
--> MysqlSelectBodyParser.visit(Select_with_parensContext);
--> AbstractParseTreeVisitor.visit(ParseTree);
--> Select_with_parensContext.accept(MysqlSelectBodyParser);
--> MysqlSelectBodyParser.visitSelect_with_parens();
Antlr Listener 与 Visitor 模式比较
Reference:
tomassetti.me/listeners-a…