访问者模式是一种将数据操作与数据结构分离的设计模式;
定义:
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作;
使用场景:
UML类图:
Visitor:
接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法理论上与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁的修改Visitor接口,如果出现这种情况,则说明不适合使用访问者模式;
ConcreteVisitor:
具体的访问者,它需要给出每一个元素类访问时所产生的具体行为;
Element:
元素接口或者抽象类,它定义了一个接口访问者(accept)的方法,表示每一个元素都要可以被访问者访问;
ElementA、ElementB:
具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法;
ObjectStructure:
对象结构,内部管理了元素集合,并且可以迭代这些元素供访问者访问;
这里我们以工作进行举例,假设一个公司里基层工作员工有两个角色,工程师
、项目经理
,管理者有组长
和部门主管
,对于工程师
和项目经理
自己手上都有对应的项目数量
,工程师还有代码行数
,而项目经理则是对应的项目质量
,现在一年一次的年度报表进行整理,对于组长
而言,他不仅需要知道工程师干了多少个项目,还需要知道写了多少代码,同时对于项目经理,他也要知道干了多少个项目,而且要知道项目质量如何,而对于主管
,大boss,他只关心基层工作干了多少个项目;下面我们就针对这种情况采用访问者设计模式进行演示:
Staff
/**
* 员工基类,访问者模式中担任Element角色
* @param name 员工姓名
* @param projectNum 项目数量
*/
abstract class Staff(val name: String, val projectNum: Int) {
abstract fun accept(visitor: Visitor)
}
Visitor
/**
* Visitor访问接口
*/
interface Visitor {
/**
* 访问工程师
*/
fun visit(engineer: Engineer)
/**
* 访问项目经理
*/
fun visit(projectManager: ProjectManager)
}
Engineer
/**
* 工程师
*/
class Engineer(name: String, projectNum: Int) : Staff(name, projectNum) {
override fun accept(visitor: Visitor) {
visitor.visit(this)
}
/**
* 代码的行数
*/
fun getCodeLines(): Int {
return Random.nextInt(10 * 10000)
}
}
ProjectManager
/**
* 项目经理
*/
class ProjectManager( name: String, productNum: Int) :
Staff(name, productNum) {
override fun accept(visitor: Visitor) {
visitor.visit(this)
}
/**
* 获取项目质量 0-10分
*/
fun getProjectQuality(): Int {
return Random.nextInt(10)
}
}
GroupLeader
/**
* 组长角色
* 对于开发人员,组长即关心手上几个项目,还关心代码函数
* 对于项目经理,组长即关心手上几个项目,还关心项目质量
*/
class GroupLeader : Visitor {
override fun visit(engineer: Engineer) {
println("${engineer.name} 项目数量:${engineer.projectNum} 代码行数:${engineer.getCodeLines()}")
}
override fun visit(projectManager: ProjectManager) {
println("${projectManager.name} 项目数量:${projectManager.projectNum} 项目质量:${projectManager.getProjectQuality()}")
}
}
Manager
/**
* 主管角色
* 对于开发人员,主管只关注手上几个项目
* 对于项目经理,主管只关注手上几个项目
*/
class Manager : Visitor {
override fun visit(engineer: Engineer) {
println("${engineer.name} 项目数量:${engineer.projectNum}")
}
override fun visit(projectManager: ProjectManager) {
println("${projectManager.name} 项目数量:${projectManager.projectNum}")
}
}
ObjectStructure
角色,YearReport
/**
* 年度报表,相当于UML图中的ObjectStructure
*/
class YearReport {
private val staffs = arrayListOf<Staff>()
init {
staffs.add(ProjectManager("张经理", 3))
staffs.add(ProjectManager("李经理", 4))
staffs.add(Engineer("赵同学", 10))
staffs.add(Engineer("王同学", 2))
staffs.add(Engineer("孙同学", 6))
}
fun showYearReport(visitor: Visitor) {
for (staff in staffs) {
staff.accept(visitor)
}
}
}
object Test {
@JvmStatic
fun main(args: Array<String>) {
val yearReport = YearReport()
//组长角色查看报表
println("-----------组长查看报表------------")
yearReport.showYearReport(GroupLeader())
println("---------------------------------------------------")
println("-----------主管查看报表------------")
//主管角色查看报表
yearReport.showYearReport(Manager())
}
}
最终输出结果:
-----------组长查看报表------------
张经理 项目数量:3 项目质量:8
李经理 项目数量:4 项目质量:3
赵同学 项目数量:10 代码行数:79325
王同学 项目数量:2 代码行数:84191
孙同学 项目数量:6 代码行数:69203
---------------------------------------------------
-----------主管查看报表------------
张经理 项目数量:3
李经理 项目数量:4
赵同学 项目数量:10
王同学 项目数量:2
孙同学 项目数量:6
APT注解解析时使用到的Element
,组成代码的基本元素有包、类、函数等等,JDK为这些元素定义了一个基类Element
public interface Element extends AnnotatedConstruct {
TypeMirror asType();
ElementKind getKind();
Set<Modifier> getModifiers();
Name getSimpleName();
Element getEnclosingElement();
List<? extends Element> getEnclosedElements();
boolean equals(Object var1);
int hashCode();
List<? extends AnnotationMirror> getAnnotationMirrors();
<A extends Annotation> A getAnnotation(Class<A> var1);
//和访问者模式一样,有一个accept方法,允许ElementVisitor进行访问
<R, P> R accept(ElementVisitor<R, P> var1, P var2);
}
ElementVisitor
public interface ElementVisitor<R, P> {
R visit(Element var1, P var2);
//访问元素
default R visit(Element e) {
return this.visit(e, (Object)null);
}
//访问包元素
R visitPackage(PackageElement var1, P var2);
//访问类型元素
R visitType(TypeElement var1, P var2);
//访问变量类型
R visitVariable(VariableElement var1, P var2);
R visitExecutable(ExecutableElement var1, P var2);
R visitTypeParameter(TypeParameterElement var1, P var2);
R visitUnknown(Element var1, P var2);
default R visitModule(ModuleElement e, P p) {
return this.visitUnknown(e, p);
}
}
优点:
缺点:
区别对待
而依赖了具体类,没有依赖抽象;如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )