Android设计模式详解之访问者模式

news2025/1/11 14:25:39

前言

访问者模式是一种将数据操作与数据结构分离的设计模式;

定义:封装一些作用于某种数据结构中的各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作;

使用场景:

  • 对象结构比较稳定,但经常需要在此对象结构上定义新的操作;
  • 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免这些操作“污染”这些对象的类,也不希望在增加新操作时修改这些类;

UML类图:
访问者模式UML
Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法理论上与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁的修改Visitor接口,如果出现这种情况,则说明不适合使用访问者模式;

ConcreteVisitor:具体的访问者,它需要给出每一个元素类访问时所产生的具体行为;

Element:元素接口或者抽象类,它定义了一个接口访问者(accept)的方法,表示每一个元素都要可以被访问者访问;

ElementA、ElementB:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法;

ObjectStructure:对象结构,内部管理了元素集合,并且可以迭代这些元素供访问者访问;

示例代码

这里我们以工作进行举例,假设一个公司里基层工作员工有两个角色,工程师项目经理,管理者有组长部门主管,对于工程师项目经理自己手上都有对应的项目数量,工程师还有代码行数,而项目经理则是对应的项目质量,现在一年一次的年度报表进行整理,对于组长而言,他不仅需要知道工程师干了多少个项目,还需要知道写了多少代码,同时对于项目经理,他也要知道干了多少个项目,而且要知道项目质量如何,而对于主管,大boss,他只关心基层工作干了多少个项目;下面我们就针对这种情况采用访问者设计模式进行演示:

  • 定义员工基类,访问者模式中担任Element角色,Staff
/**
 * 员工基类,访问者模式中担任Element角色
 * @param name 员工姓名
 * @param projectNum 项目数量
 */
abstract class Staff(val name: String, val projectNum: Int) {

    abstract fun accept(visitor: Visitor)

}
  • 定义Visitor访问接口,Visitor
/**
 * Visitor访问接口
 */
interface Visitor {
    /**
     * 访问工程师
     */
    fun visit(engineer: Engineer)

    /**
     * 访问项目经理
     */
    fun visit(projectManager: ProjectManager)
}
  • 定义工程师类,访问者模式中担任具体的Element角色,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)
    }

}
  • 定义项目经理类,访问者模式中担任具体的Element角色,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

Android源码中的访问者模式

  • 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);
    }
}

总结

优点:

  • 各角色职责分离,符合单一职责原则;
  • 具有优秀的扩展性;
  • 使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化;
  • 灵活性;

缺点:

  • 具体元素对访问者公布细节,违反了迪米特原则;
  • 具体元素变更时导致修改成本太大;
  • 违反了依赖导致原则,为了达到区别对待而依赖了具体类,没有依赖抽象;

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/118266.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大厂与小厂招人的区别,看完多少有点不敢相信

前两天在头条发了一条招人的感慨&#xff0c;关于大厂招人和小公司招人的区别。 大厂&#xff1a;有影响力&#xff0c;有钱&#xff0c;能够吸引了大量的应聘者。因此&#xff0c;也就有了筛选的资格&#xff0c;比如必须985名校毕业&#xff0c;必须35岁以下&#xff0c;不能…

基于DoIP使用CANoe对ECU进行诊断测试

伴随以太网引入到车载网络中,本文分享通过常用工具CANoe怎么样对ECU进行通信以及测试。 相比在车载CAN总线,以太网又有什么与众不同之处? 1、硬件接口卡(收发器) 以往车载CAN网络较常使用的是VN 16XX 系列,在连接ECU进行通信时,除了配置波特率也要进行通道分配: 而…

7个学习UI、UX设计一定要经历的步骤

我们不是一些有才华的设计师。我们天生就有艺术天赋。后天我们学会了设计技巧。设计的根本目的是解决问题。设计是不断发现和解决问题。 有许多设计领域&#xff1a;UI、UX.产品设计师.平面设计师.交互设计师.信息架构师等&#xff0c;所以要找出你最感兴趣的设计专业。 现在让…

美颜sdk动态贴纸技术、代码分析

目前&#xff0c;美颜sdk动态贴纸已经成了各大直播平台主播的必备“直播伴侣”&#xff0c;在其他的视频拍摄场景动态贴纸的热度同样很高&#xff0c;本篇文章小编将为大家深度盘点一下美颜sdk动态贴纸的技术实现以及代码。 一、多终端适配 对于如今的直播平台终端来说&#x…

CAPL学习之路-测试功能集函数(测试结构化)

用户可以使用如下函数在测试报告中对每一条测试用例设置结构化的输出内容 TestCaseDescription 添加测试用例的描述文本 此函数用于测试用例中,描述文本会添加在固定区域(测试用例title的下方)。多次调用该函数,描述文本会合并显示在固定区域。如果想让描述文本换行,可以…

爆火的Web3.0背后,百度营销如何抓住流量密码?

出品| 大力财经 文 | 魏力 AI、元宇宙、Web3.0、AIGC等新技术、新概念的加持&#xff0c;给传统的流量营销平台带来了前所未有的挑战。尤其是短视频时代的崛起&#xff0c;用户的使用习惯开始改变&#xff0c;完全改变了流量的逻辑和习惯。 从搜索引擎业务起家的百度&#x…

DoIP---车载以太网诊断方面边缘节点的路由策略分析

假期后开工第一天&#xff0c;规划好自己一天需要做的事情&#xff0c;按部就班完成每日任务&#xff0c;做好每日总结。 自己一天一个脚印&#xff0c;这不是鸡血&#xff0c;这是工作态度&#xff01;&#xff01;&#xff01; 惯例分享一段喜欢的文字&#xff1a; 每个人…

目标检测之FCOS算法分析

网络结构 (图片来自原论文&#xff1a;FCOS: Fully Convolutional One-Stage Object Detection) 在ResNet50 Backbone中&#xff0c;C3,C4,C5C3,C4,C5C3,C4,C5是卷积特征图&#xff1b; 在FPN结构中&#xff0c;P3,P4,P5,P6,P7P3,P4,P5,P6,P7P3,P4,P5,P6,P7是最后用于预测的特…

2023跨境出海指南:泰国网红营销白皮书

作为东南亚第二大经济体&#xff0c;泰国一直是旅游和企业出海的热门之地。随着电商经济和互联网的发展&#xff0c;加上疫情的催化&#xff0c;泰国的社交媒体行业也得到了飞速发展&#xff0c;已经成为了主流营销方式之一。本文Nox聚星就从网红营销的角度&#xff0c;和大家探…

代码随想录-46-226.翻转二叉树

目录前言题目1.使用队列思路&#xff08;定义变量&#xff09;2. 本题思路分析&#xff1a;3. 算法实现4. pop函数的算法复杂度5. 算法坑点前言 在本科毕设结束后&#xff0c;我开始刷卡哥的“代码随想录”&#xff0c;每天一节。自己的总结笔记均会放在“算法刷题-代码随想录…

浅谈一下个人基于IRIS后端业务开发框架的理解

文章目录浅谈一下个人基于IRIS后端业务开发框架的理解现状方案具体实现BaseBizDataFilterSqlImp、RefApiUtil总结浅谈一下个人基于IRIS后端业务开发框架的理解现状由于国内使用基于M语言IRIS平台几乎都在医疗行业。医疗系统又非常的庞大和复杂。前期由于快速占领市场&#xff0…

珠城科技在创业板上市:IPO首日跌破发行价,市值相对蒸发约7亿元

12月26日&#xff0c;浙江珠城科技股份有限公司&#xff08;下称“珠城科技”&#xff0c;SZ:301280&#xff09;在深圳证券交易所创业板上市。本次上市&#xff0c;珠城科技的发行价格为67.40元/股&#xff0c;发行数量为1628.34万股&#xff0c;募资总额约为10.98亿元&#x…

java线程

1.创建线程和运行线程 1.1.方式一: 直接使用Thread线程对象创建线程 Slf4j public class TestThread {public static void main(String[] args) {//创建一个线程,并且指定线程名称为"t1"Thread thread new Thread("t1") {Overridepublic void run() {//…

基于JAVA springboot + MYSQL +VUE的项目管理系统(含数据库),包括工时统计、原型预览、效果图管理等

平台介绍 无鱼工时管理系统&#xff0c;是一款轻量级工时记录和管理工具&#xff0c;包括项目管理&#xff0c;工时上报&#xff0c;工时日报&#xff0c;工时统计等功能。 无鱼工时管理系统可通过员工工时上报的方式&#xff0c;来记录项目所花费的工时&#xff0c;帮助企业…

滑块验证 - 使用AJ-Captcha插件【超简单.jpg】

滑块验证实现一、后端1&#xff09;首先引入maven&#xff1a;2&#xff09;再在application.yml中自定义水印&#xff0c;直接启动后前端就可以请求接口了3&#xff09;重写CaptchaCacheServiceRedisImpl①先新建一个文件夹②重写impl二、前端&#xff1a;1&#xff09;复制文…

UML2面向对象分析与设计(第2版) 谭火彬 杂记

首先&#xff0c;来讲讲我对泛化的理解&#xff0c;其实这是站在的视角的不同而表述的不同&#xff0c;泛化是站在父类的角度&#xff0c;父类给孩子的方式叫泛化&#xff0c;而继承是站在孩子的角度&#xff0c;儿子继承父类的方式叫继承。 其实上了谭老师大概一章的课程&…

使用WebPageTest、Lighthouse和Chrome DevTools评估网站性能

目录 一&#xff1a;使用WebPageTest评估网站性能 二&#xff1a;使用Lighthouse分析性能 1、本地npm安装Lighthouse 2、Chrome DevTools中使用 三&#xff1a;使用Chrome DevTools分析性能 一&#xff1a;使用WebPageTest评估网站性能 进入网站首页WebPageTest - Websit…

Linux下常用基本指令大全

在XShell下的复制粘贴 复制: ctrl insert (有些insert 需要配合fn 来按) 粘贴: shift insert ctrl c / ctrl v 是不行的.1. ls指令 语法&#xff1a;ls [选项][目录或文件] 功能&#xff1a;对于目录&#xff0c;该命令列出该目录下的所有子目录与文件。对于文件&#xff0…

从App Lab就一鸣惊人的Gorilla Tag,创始人竟是个电竞选手

前不久&#xff0c;热门VR游戏《Gorilla Tag》登陆Quest正式商店&#xff0c;此前该作已经进入App Lab和SteamVR平台&#xff0c;一度成为App Lab最受欢迎的游戏&#xff0c;至此终于修成正果。截至目前&#xff0c;该作在全平台的下载量高达500万次&#xff0c;在Quest商店累计…

七、Java 14 新特性

七、Java 14 新特性 Java 14 已如期于 2020 年 3 月 17 日正式发布&#xff0c;此次更新是继半年前 Java 13 这大版本发布之后的又一次常规版本更新&#xff0c;即便在全球疫情如此严峻形势下&#xff0c;依然保持每六个月的版本更新频率&#xff0c;为大家及时带来改进和增强&…