摘要:本文用一个实例场景描述Gof 23设计模式中的访问者(Visitor)模式,并用Quarkus框架代码给予实现,同时也给出实现代码的UML模型。
关键字:Gof 23 设计模式 访问者模式 Quarkus
1 基础知识
1.1 标准定义
访问者(Visitor)模式标准定义:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
1.2 分析和说明
访问者(Visitor)模式属于对象行为型模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。
访问者(Visitor)模式结构如图1所示,其角色包括抽象访问者(Visitor)角色、具体访问者(Concrete Visitor)角色、抽象节点(Node)角色、具体节点(Node)角色和结构对象(Object Structure)角色。
图1 访问者模式结构
抽象访问者(Visitor)角色:声明了一个或者多个访问操作,形成所有的具体元素角色必须实现的接口。
具体访问者(Concrete Visitor)角色:实现抽象访问者角色所声明的接口,也就是抽象访问者所声明的各个访问操作。
抽象节点(Node)角色:声明一个接受操作,接受一个访问者对象作为一个参量。
具体节点(Node)角色:实现了抽象元素所规定的接受操作。
结构对象(Object Structure)角色:有如下的一些责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如列(List)或集合(Set)。
2 应用场景举例
比如公司一般都要接受多方面的审查,对于工商部门,看看是否符合商务审计。对于税务部门,看看是否合法纳税。对于会计师事务所,要对公司进行财务审计。这些部门都是外部的参观者。抽象访问者(Visitor)角色:工商部门、税务部门和会计师事务所是具体访问者角色。定义一个抽象公司的抽象节点角色。不同的公司工商情况,税务情况和会计情况就是具体节点角色。用例如图2所示。
图2 访问者模式类图
在这里可以把Visitor抽象类理解为抽象访问者(Visitor)角色。AccountingFirm类、TaxBureau类、TradeBureau类是具体访问者(Concrete Visitor)角色。AbstractCompany抽象类是抽象节点(Node)角色。CompanyA类和CompanyB类是具体节点(Node)角色。其结构类图如图3所示。AccountingFirm类、TaxBureau类、TradeBureau类继承Visitor抽象类,CompanyA类和CompanyB类继承AbstractCompany抽象类。AbstractCompany抽象类关联Visitor抽象类。
图3 访问者模式类图
访问者模式实现顺序图见图4,实现顺序描述:① 基于CompanyA类创建一个company1对象;② 基于CompanyB类创建一个company2对象;③ 基于AccountingFirm类创建一个accountingFirm对象;④ 基于TaxBureau类创建一个taxBureau对象;⑤ 基于TradeBureau类创建一个tradeBureau对象;⑥ 调用company1对象的accept方法,把accountingFirm对象赋值给company1对象;⑦ 调用company1对象的doVisit方法,显示参观内容;⑧ 调用company1对象的accept方法,把taxBureau对象赋值给company1对象;⑨ 调用company1对象的doVisit方法,显示参观内容;⑩ 调用company1对象的accept方法,把tradeBureau对象赋值给company1对象;(11)调用company1对象的doVisit方法,显示参观内容;(12)调用company1对象的accept方法,把accountingFirm对象赋值给company1对象;(13) 调用company1对象的doVisit方法,显示参观内容。(14) 调用company2对象的accept方法,把taxBureau对象赋值给company1对象;(15)调用company2对象的doVisit方法,显示参观内容。(16) 调用company2对象的accept方法,把tradeBureau对象赋值给company1对象;(17)调用company2对象的doVisit方法,显示参观内容。
图4 访问者模式实现顺序图
3.Quarkus的实现程序代码
Quarkus程序实现主要包括Visitor抽象类文件,AbstractCompany抽象类文件,AccountingFirm类文件、TaxBureau类文件、TradeBureau类文件,CompanyA类文件和CompanyB类文件等7个文件。其关系如图3所示。下面分别列出这7个文件的程序代码,最后列出测试代码并显示输出结果。
Visitor类程序代码清单01所示。
程序代码清单01
public abstract class Visitor {
public void visitCompanyA(CompanyA company){}
public void visitCompanyB(CompanyB company){}
}
AbstractCompany抽象类程序代码清单02所示。
程序代码清单02
public abstract class AbstractCompany {
protected Visitor vistor;
public void accept(Visitor vistor){
this.vistor = vistor ;
}
public void doVisit(){}
}
AccountingFirm类、TaxBureau类、TradeBureau类文件继承Visitor抽象类,其程序代码清单01所示。
程序代码清单03
@ApplicationScoped
public class AccountingFirm extends Visitor{
public void VisitCompanyA(CompanyA company){
System.out.println("对CompanyA类公司进行会计审计工作");
}
public void VisitCompanyB(CompanyB company){
System.out.println("对CompanyB类公司进行会计审计工作");
}
}
@ApplicationScoped
public class TaxBureau extends Visitor {
public void VisitCompanyA(CompanyA company) {
System.out.println("对CompanyA类公司进行税务审计工作");
}
public void VisitCompanyB(CompanyB company) {
System.out.println("对CompanyB类公司进行税务审计工作");
}
}
@ApplicationScoped
public class TradeBureau extends Visitor {
public void VisitCompanyA(CompanyA company) {
System.out.println("对CompanyA类公司进行工商年审工作");
}
public void VisitCompanyB(CompanyB company) {
System.out.println("对CompanyB类公司进行工商年审工作");
}
}
抽象公司的类程序代码清单01所示。
程序代码清单04
@ApplicationScoped
public class CompanyA extends AbstractCompany{
public void VisitA(){}
public void doVisit(){
vistor.VisitCompanyA(this);
}
}
@ApplicationScoped
public class CompanyB extends AbstractCompany{
public void doVisit(){
vistor.VisitCompanyB(this);
}
}
访问者模式测试程序的代码清单12如下:
程序代码清单05
public class VisitorClient implements QuarkusApplication {
@ConfigProperty(name = "gof23.behavioralpattern.visitor.title", defaultValue = "gof23")
String title;
@Inject CompanyA company1;
@Inject CompanyB company2;
@Inject AccountingFirm accountingFirm;
@Inject TaxBureau taxBureau;
@Inject TradeBureau tradeBureau;
@Override
public int run(String... args){
System.out.println("——————" + title + "演示输出————————");
System.out.println("————对CompanyA类公司进行处理————");
company1.Accept(accountingFirm);
company1.doVisit();
company1.Accept(taxBureau);
company1.doVisit();
company1.Accept(tradeBureau);
company1.doVisit();
System.out.println("————对CompanyB类公司进行处理————");
company2.Accept(accountingFirm);
company2.doVisit();
company2.Accept(taxBureau);
company2.doVisit();
company2.Accept(tradeBureau);
company2.doVisit();
return 0;
}
public static void main(String... args) {
Quarkus.run(VisitorClient.class, args);
}
}
访问者模式测试类输出结果如下所示:
————对CompanyA类公司进行处理————
对CompanyA类公司进行会计审计工作
对CompanyA类公司进行税务审计工作
对CompanyA类公司进行工商年审工作
————对CompanyB类公司进行处理————
对CompanyB类公司进行会计审计工作
对CompanyB类公司进行税务审计工作
对CompanyB类公司进行工商年审工作
4. 相关Quarkus程序源码下载
可以直接从github上获取代码,读者可以从github上clone预先准备好的示例代码。
git clone https://github.com/rengang66/quarkus-sample-gof23.git
这是一个Maven项目,然后Maven导入工程。该程序位于“src\main\java\com\iiit\quarkus\sample\gof23\behavioralpattern\visitor”目录中。
同时也可以从gitee上clone预先准备好的示例代码,命令如下:
git clone https://gitee.com/rengang66/quarkus-sample-gof23.git
参考文献
[1] E.Gamma, R.Helm, R.Johnson, and Vlissides. Design Patterns Elements of Reusable Object Oriented Software. Addison-Wesley, 1995
[2] E.Gamma, R.Helm, R.Johnson, and Vlissides.著,李英军等译,设计模式:可复用面向对象软件的基础,北京:机械工业出版社.2000.9.
[3] 阎宏,Java与模式,北京:电子工业出版社. 2002.10
[4] 王俊峰 戚晓滨. 设计模式和UML. 计算机应用研究,1999.16(5), 27-29,39.
[5] 陈琴 朱正强. UML在设计模式描述中的应用. 计算机工程与设计,2003.24(4), 81-84.
[6] 郑建华 李迪 肖苏华. 访问者设计模式在模型转换中的应用研究. 制造业自动化,2008.30(11), 41-43,50.
[7] 赵红超 方金云 唐志敏. Visitor模式在GIS要素绘制中的应用. 计算机工程与应用,2005.41(6), 95-97,126.
[8] 刘征驰 杨贯中. Visitor设计模式研究. 计算机工程,2005.31(8), 84-86.
[9] 刘京锐 孙纪安. 管线到访问器的新设计模式. 程序员,2006.(8), 150-153.
[10] 殷定媛 高建华. 软件重构中Visitor*设计模式和应用. 计算机工程与设计,2006.27(24), 4817-4820.
[11] 苏姝 李霖 刘庆华. 设计模式在GIS系统开发中的运用. 测绘科学,2006.31(3), 99-100,103.
[12] Quarkus官网. https://quarkus.io/