Android设计模式详解之命令模式

news2024/12/24 20:29:01

前言

命令模式是行为型设计模式之一;

定义:将一个请求封装成一个对象,从而让用户使用不同的请求把客户端参数化;对请求排队或者记录日志,以及支持可撤销的操作;

使用场景:

  • 需要抽象出待执行的动作,然后以参数的形式提供出来,类似于过程设计中的回调机制,而命令模式正是回调机制的一个面对对象的替代品;
  • 在不同的时刻指定、排列和执行请求。一个命令对象可以有与初始请求无关的生存期;
  • 需要支持取消操作;
  • 支持修改日志功能,这样当系统崩溃时,这些修改可以被重做一遍;
  • 需要支持事务操作;

UML类图:
命令模式UML类图

  • Client:客户端角色;
  • Invoker:请求者角色;
    该类的职责就是调用命令对象执行具体的请求,相关的方法我们称为行动方法;
  • Receiver:接收者角色;
    该类负责具体实施或执行一个请求,执行具体逻辑的角色,具体操作逻辑的方法我们称为行动方法;
  • Command:命令角色;
    定义所有具体命令类的抽象接口;
  • ConcreteCommand:具体命令角色;
    该类实现了Command接口,在execute方法中调用接收者角色的相关方法,在接收者和命令执行的具体行为之间加以弱耦合。

通用模板代码

  • 定义接收者角色,Receiver
/**
 * 接收者类
 */
class Receiver {
    //最终执行具体命令逻辑的方法
    fun action() {
        println("执行具体操作")
    }
}
  • 定义抽象命令接口,Command
/**
 * 抽象命令接口
 */
interface Command {
    //执行具体操作的命令
    fun execute()
}
  • 定义具体命令类,ConcreteCommand
/**
 * 具体命令类
 * @param receiver 持有一个接收者对象的引用
 */
class ConcreteCommand(private var receiver: Receiver) : Command {
    override fun execute() {
        //调用接收者的相关方法执行具体逻辑
        receiver.action()
    }

}
  • 定义请求者类,Invoker
/**
 * 请求者类
 * @param command 持有一个对命令对象的引用
 */
class Invoker(private val command: Command) {
    fun action() {
        //调用具体命令对象的相关方法,执行具体命令
        command.execute()
    }
}
  • 编写调用测试类,相当于Client角色
/**
 * 相当于Client类
 */
object Test {

    @JvmStatic
    fun main(args: Array<String>) {
        //构造一个接收者对象
        val receiver = Receiver()
        //根据接收者对象构造一个命令对象
        val concreteCommand = ConcreteCommand(receiver)
        //根据命令对象构造一个请求者对象
        val invoker = Invoker(concreteCommand)
        //执行请求方法
        invoker.action()
    }
}

实现实例

这里我们以游戏中上下左右四个按键进行举例四个按键即对应四个命令,分别按下对应向左向右向上向下命令,这里我们使用命令模式来实现;

  • 首先,定义命令接收者,即最终执行命令的对象,CommandReceiver

/**
 * 命令接收者对象
 */
class CommandReceiver {

    fun toLeft() {
        println("向左")
    }

    fun toTop() {
        println("向上")
    }

    fun toRight() {
        println("向右")
    }


    fun toBottom() {
        println("向下")
    }
}
  • 定义抽象命令,Command
/**
 * 抽象命令
 */
interface Command {
   fun execute()
}
  • 定义具体命令,LeftCommandTopCommandRightCommandBottomCommand
/**
 * 具体向左命令
 */
class LeftCommand(private val commandReceiver: CommandReceiver) : Command {

    override fun execute() {
        commandReceiver.toLeft()
    }
}

/**
 * 具体向上命令
 */
class TopCommand(private val commandReceiver: CommandReceiver) : Command {

    override fun execute() {
        commandReceiver.toTop()
    }
}

/**
 * 具体向右命令
 */
class RightCommand(private val commandReceiver: CommandReceiver) : Command {

    override fun execute() {
        commandReceiver.toRight()
    }
}

/**
 * 具体向下命令
 */
class BottomCommand(private val commandReceiver: CommandReceiver) : Command {

    override fun execute() {
        commandReceiver.toBottom()
    }
}
  • 定义命令发起者,CommandInvoker
/**
 * 命令请求者
 */
class CommandInvoker {

    private var leftCommand: LeftCommand? = null
    private var topCommand: TopCommand? = null
    private var rightCommand: RightCommand? = null
    private var bottomCommand: BottomCommand? = null

    fun setLeftCommand(leftCommand: LeftCommand) {
        this.leftCommand = leftCommand
    }

    fun setTopCommand(topCommand: TopCommand) {
        this.topCommand = topCommand
    }

    fun setRightCommand(rightCommand: RightCommand) {
        this.rightCommand = rightCommand
    }

    fun setBottomCommand(bottomCommand: BottomCommand) {
        this.bottomCommand = bottomCommand
    }

    /**
     * 请求执行向左命令
     */
    fun executeLeftAction() {
        leftCommand?.execute()
    }

    /**
     * 请求执行向上命令
     */
    fun executeTopAction() {
        topCommand?.execute()
    }

    /**
     * 请求执行向右命令
     */
    fun executeRightAction() {
        rightCommand?.execute()
    }

    /**
     * 请求执行向下命令
     */
    fun executeBottomAction() {
        bottomCommand?.execute()
    }
}
  • 编写测试类进行验证;
object Test {

    @JvmStatic
    fun main(args: Array<String>) {
        //构造一个接收者对象
        val receiver = CommandReceiver()
        //根据接收者对象构造一个命令对象
        val leftCommand = LeftCommand(receiver)
        val topCommand = TopCommand(receiver)
        val rightCommand = RightCommand(receiver)
        val bottomCommand = BottomCommand(receiver)
        //根据命令对象构造一个请求者对象
        val invoker = CommandInvoker()
        invoker.setLeftCommand(leftCommand)
        invoker.setTopCommand(topCommand)
        invoker.setRightCommand(rightCommand)
        invoker.setBottomCommand(bottomCommand)
        //执行 `上上下下左右左右`
        invoker.executeTopAction()
        invoker.executeTopAction()
        invoker.executeBottomAction()
        invoker.executeBottomAction()
        invoker.executeLeftAction()
        invoker.executeRightAction()
        invoker.executeLeftAction()
        invoker.executeRightAction()
    }


}

前面我们有提到过,命令模式支持修改日志和事务操作,如上面例子,我们可以在CommandInvoker类中记录每一步执行的操作进行日志记录,同时也可以根据记录的操作进行事务提交;

Android源码中的命令模式

  • Application Framework(应用程序框架层)中PackageManagerService类(包管理部分)也用到了命令模式。PackageManagerService是Android系统的Service之一,主要功能是实现对应用包的解析、管理、卸载等操作。具体的包的安装、移动以及包大小的测量分别在3个具体子类InstallParams、MoveParams和MeasureParams中实现。

总结

优点:
低耦合、灵活的控制性、更好的扩展性;

缺点:
大量类的创建,造成类的膨胀;

结语

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

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

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

相关文章

自动化测试高手-价值篇

自动化测试的最终交付价值是什么&#xff1a;自动化测试项目的最终交付价值是它产生的收益&#xff0c;也就是投入回报率比&#xff0c;ROI&#xff0c;一个成功的自动化测试项目必然是获得了高ROI的收益。 自动化测试高手就是要做出成功的自动化测试项目&#xff0c;不仅要写代…

linux命令date

linux命令date 文章目录linux命令date一、date二、使用&#xff08;一&#xff09;结合格式&#xff0c;格式输出数据&#xff08;二&#xff09;结合选项&#xff08;options&#xff09;一、date date命令可以获取到当前系统时间&#xff0c;主要是为了shell脚本的 date用法…

Html中锚点的使用

概述 URL是由多个部分组成。包括协议、主机&#xff08;域名&#xff09;、端口、路径、查询参数和锚点 https://www.example.com:80/path/to/myfile.html?key1value1&key2value2#anchor锚点&#xff08;anchor&#xff09;是网页内部的定位点&#xff0c;使用 # 加上锚…

3_Spring_IOC原理分析_重要

IOC底层原理 1 XML解析技术读取配置文件 <bean id"empDao" class"com.msb.dao.impl.EmpDaoImpl"></bean> 将上面的信息读取进入程序 对象的ID ,一个是对象的类的全路径名 2 反射技术实例化对象,放到容器中 获得类的字节码 Class clazz Clas…

论文导读 | GPU与LSM-tree的优化--以加速compaction和scan为例

GPU是一种新硬件&#xff0c;相比较于CPU&#xff0c;有较高的读写带宽和更强的并行能力&#xff0c;在很多领域都有非常好的应用。今天我们以LSM-tree上的compaction和scan为例&#xff0c;介绍GPU如何在加速数据库的操作中发挥作用。 一、GPU的发展历程 图1&#xff1a;GPU的…

基于uniapp+ssm+mysql一站式婚庆服务平台app设计

开发软件&#xff1a;Idea HBuilder mysql 开发技术&#xff1a; SSM Vue uniapp 用户功能&#xff1a; 注册登陆&#xff0c;填写基本的个人信息&#xff0c;就可以注册&#xff0c;注册完成后即可登陆 查看店铺&#xff0c;在首页有很多的店铺&#xff0c;有婚纱店&…

PHP 精度计算问题(精确算法)

1. PHP 中的精度计算问题 当使用 php 中的 -*/ 计算浮点数时, 可能会遇到一些计算结果错误的问题 这个其实是计算机底层二进制无法精确表示浮点数的一个 bug, 是跨域语言的, 比如 js 中的 舍入误差 所以大部分语言都提供了用于精准计算的类库或函数库, 比如 php 中的 bc 高精…

逻辑回归、激活函数sigmoid、损失及优化、案例代码实现

一、逻辑回归 逻辑回归&#xff08;Logistic Regression&#xff09;&#xff1a;是机器学习中的一种分类模型&#xff0c;是一种分类算法&#xff0c;与回归之间有一定的联系&#xff0c;由于算法的简单和高效&#xff0c;在实际中应用非常广泛 应用场景&#xff1a;广告点击…

Web入门开发【八】- 学习探索

Web案例详解 简单的个人静态网站&#xff0c;这是一个单页的简单静态web网站&#xff0c;前面已经详细讲过。 这个案例是动态的企业官网&#xff0c;增加了多菜单&#xff0c;动态登录等功能&#xff0c;是入门学习者进阶的实践必做项目。 我们再来看企业后台管理系统&#xff…

Python Yolov5火焰烟雾识别源码分享

程序示例精选 Python Yolov5火焰烟雾识别 如需安装运行环境或远程调试&#xff0c;见文章底部微信名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 Yolov5比较Yolov4,Yolov3等其他识别框架&#xff0c;速度快&#xff0c;代码结构简单&#xff0c;识别效率高&#x…

操作系统开启分段并进入保护模式

段基地址 32位的地址&#xff0c;如果没开启分页&#xff0c;指的是当前段所在的物理地址&#xff0c;否则是分页前的虚拟地址 G(Granurality) 值为1表示段界限以4K为单位&#xff0c;否则以字节为单位 段界限 描述段的大小size-1,单位由G决定。 D/B 对于代码段&#xf…

【Linux】缓冲区/磁盘inode/动静态库

目录 一、缓冲区 1、缓冲区的概念 2、缓冲区的意义 3、缓冲区刷新策略 4、同一份代码&#xff0c;打印结果不同 5、仿写FILE 5.1myFILE.h 5.2myFILE.c 5.3main.c 6、内核缓冲区 二、了解磁盘 1、磁盘的物理结构 2、磁盘的存储结构 2.1磁盘的定位 3、磁盘的抽象…

【openGauss】浅试openGauss3.1.0中有关mysql兼容的部分特性

前言 在9月30号&#xff0c;openGauss推出了3.1.0这一预览版&#xff08;注意&#xff0c;openGauss的“x.y.z”版本号&#xff0c;“y”的位置如果不是0&#xff0c;就不是长期支持版&#xff0c;不建议生产使用&#xff09;。 这个版本增加了不少新内容&#xff0c; https:/…

【KGAT】Knowledge Graph Attention Network for Recommendation

note 其实不结合KG&#xff0c;何向南团队之前也直接使用GCN做了NGCF和LightGCN。KGAT结合KG和GAT&#xff0c;首先是CKG嵌入表示层使用TransR模型获得实体和关系的embedding&#xff1b;然后在attention表示传播层&#xff0c;使用attention求出每个邻居节点的贡献权重&#…

35岁有儿有女,为什么我开始自学编程?

零基础编程入门越来越容易 这么讲并不夸张&#xff1a;无论你初学哪门编程语言&#xff0c;第一行代码几乎都是打印出 Hello world ! print(Hello world!) print(Hello python!) 遥想当年&#xff0c;花上一两天折腾完各种安装配置调试环境&#xff0c;写下第一句“面世代码…

该怎么选择副业,三条建议形成自己的副业思维

受经济环境的影响&#xff0c;许多年轻人觉得原来稳定的工作不那么稳定&#xff0c;看着周围的朋友因为企业破产和失业&#xff0c;生活变得没有信心&#xff0c;也想找到自己的副业&#xff0c;在紧急情况下赚更多的钱。所以&#xff0c;年轻人在选择副业时也面临着很多困惑&a…

Java --- JUC的CompletableFuture的使用

目录 一、Future接口 二、Future接口的功能 三、FutureTask 四、CompletableFuture背景及使用 4.1、CompletionStage 4.2、CompletableFuture 4.3、四个静态方法 4.4、减少阻塞和轮询 4.5、使用CompletableFuture完成电商大比价 五、CompletableFuture常用API 5.1、获…

【华为OD机试真题 C++】TLV解析 【2022 Q4 | 100分】

■ 题目描述 TLV编码是按[Tag Length Value]格式进行编码的&#xff0c;一段码流中的信元用Tag标识&#xff0c;Tag在码流中唯一不重复&#xff0c;Length表示信元Value的长度&#xff0c;Value表示信元的值。 码流以某信元的Tag开头&#xff0c;Tag固定占一个字节&#xff0…

机器学习 | 逻辑回归

一.基本原理 面对一个分类问题&#xff0c;建立代价函数&#xff0c;通过优化方法迭代求解出最优的模型参数&#xff0c;然后测试验证我们这个求解的模型的好坏。逻辑回归是一种分类方法&#xff0c;主要用于二分类问题&#xff0c;应用于研究某些事件发生的概率 二.优缺点 …

day28【代码随想录】回溯之组合、组合总和|||、电话号码的字母组合

文章目录前言一、组合&#xff08;力扣77&#xff09;剪枝优化二、组合总和 III&#xff08;力扣216&#xff09;剪枝优化三、电话号码的字母组合&#xff08;力扣17&#xff09;总结前言 1、组合 2、组合总和||| 3、电话号码的字母组合 一、组合&#xff08;力扣77&#xff0…