原标题:Android 源码中的命令模式

(点击上方公众号,可快速关注)

来源:伯乐在线专栏作者 - PleaseCallMeCoder

链接:http://android.jobbole.com/84937/

写在前面

前面跟大家分享了装饰者模式、观察者模式、静态工厂方法、工厂方法模式,今天跟大家分享下Android源码中的命令模式。

命令模式

定义

将一个请求封装成一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。

使用场景

对于大多数请求——响应模式的功能,比较适合使用命令模式。

系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。

系统需要在不同的时间指定请求、将请求排队(如:线程池+工作队列)和执行请求。

系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作(比如系统挂掉之后重启做一些恢复操作,还有数据库的事务等)。

系统需要将一组操作组合在一起,即支持宏命令。

结构

命令模式涉及到的角色:

客户角色(Client):Client可以创建具体的命令对象,并且设置命令对象的接收者。Tips:不能把Clinet理解为我们平常说的客户端,这里的Client是一个组装命令对象和接受者对象的角色,或者你把它理解为一个装配者。

调用者角色(Invoker):负责调用命令对象执行请求,通常会持有命令对象(可以持有多个命令对象)。Invoker是Client真正触发命令并要求命令执行相应操作的地方(使用命令对象的入口)。

命令角色(Command):定义命令的接口,声明具体命令类需要执行的方法。这是一个抽象角色。

具体命令角色(ConcreteCommand):命令接口的具体实现对象,通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

接收者角色(Receiver):Receiver是真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

实现

命令模式其实就是对命令进行封装,将命令请求者和命令执行者的责任分离开来实现松耦合。这里我们通过一个餐厅点餐的实例来剖析一下命令模式:命令接收者Cook可以做各式各样的菜,根据Waiter送过来的订单来满足顾客的需求,具体命令实现类PigCook执行做烤乳猪命令,DuckCook执行烧花鸭命令等等,Client负责组装各个部分。

命令角色

publicinterfaceCommand{

publicvoidexecute();

publicvoidundo();

publicvoidredo();

}

命令接收者

publicclassCook{

//烤乳猪的方法

publicvoidcookPig(){

}

//烧花鸭的方法

publicvoidcookDuck(){

}

}

具体命令角色

//做烤乳猪的命令

publicclassPigCookimplementsCommand{

privateCookmCook;

publicPigCook(Cookcook){

mCook=cook;

}

@Override

publicvoidexecute(){

mCook.cookPig();

}

@Override

publicvoidundo(){

}

@Override

publicvoidredo(){

}

}

//做烧花鸭的命令

publicclassDuckCookimplementsCommand{

privateCookmCook;

publicDuckCook(Cookcook){

mCook=cook;

}

@Override

publicvoidexecute(){

mCook.cookDuck();

}

@Override

publicvoidundo(){

}

@Override

publicvoidredo(){

}

}

调用者角色

publicclassWaiter{

privateCommandpig;

privateCommandduck;

publicvoidsetCommandPig(Commandpig){

this.pig=pig;

}

publicvoidsetCommandDuck(Commandduck){

this.duck=duck;

}

/**

* 执行正常命令,这里省略了undo和redo操作

*/

publicvoidinvoke(intargs){

//可以根据具体情况选择执行某些命令

if(args==0){

pig.execute();

}elseif(args==1){

duck.execute();

}

}

}

客户角色

publicclassClient{

/**

* 组装操作

*/

publicvoidassembleAction(){

//创建一个命令接收者

CookmCook=newCook();

//创建一个命令的具体实现对象,并指定命令接收者

Commandpig=newPigCook(mCook);

Commandduck=newDuckCook(mCook);

WaitermWaiter=newWaiter();//创建一个命令调用者

//为调用者指定烤乳豬命令对象

mWaiter.setCommandPig(pig);

//为调用者指定烧花鸭命令对象

mWaiter.setCommandDuck(duck);

//发起调用烤乳猪命令请求

mWaiter.invoke(0);

//发起调用烧花鸭命令请求

mWaiter.invoke(1);

}

}

可是,为什么要这么复杂咧,我只是想点个菜而已嘛,直接这么搞不就好了?

publicclassClient{

/**

* 组装操作

*/

publicvoidassembleAction(){

//创建一个命令接收者

CookmCook=newCook();

//发起调用烤乳猪命令请求

mCook.cookPig();

//发起调用烧花鸭命令请求

mCook.cookDuck();

}

}

我们知道命令模式的一个优点是支持命令的撤销(Undo)操作和恢复(Redo)操作,如果我们像上边一样调用,我们要想做撤销是不是就不那么方便了呢。同时还可以考虑下命令模式的其他几个优点。

总结

每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。

命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。

命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。

命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。

Android源码中的命令模式

对于Android源码来说,Android底层逻辑对事件的转发处理就用到了命令模式。Application Framework(应用程序框架层)中PackageManagerService类(包管理部分)也用到了命令模式。PackageManagerService是Android系统的Service之一,主要功能是实现对应用包的解析、管理、卸载等操作。我们来看下具体的结构。

HandlerParams是命令接口,即我们的Command角色。

privateabstractclassHandlerParams{

privatestaticfinalintMAX_RETRIES=4;

/**

* Number of times startCopy() has been attempted and had a non-fatal

* error.

*/

privateintmRetries=0;

/** User handle for the user requesting the information or installation. */

privatefinalUserHandlemUser;

StringtraceMethod;

inttraceCookie;

HandlerParams(UserHandleuser){

mUser=user;

}

UserHandle getUser(){

returnmUser;

}

HandlerParams setTraceMethod(StringtraceMethod){

this.traceMethod=traceMethod;

returnthis;

}

HandlerParams setTraceCookie(inttraceCookie){

this.traceCookie=traceCookie;

returnthis;

}

finalbooleanstartCopy(){

booleanres;

try{

if(DEBUG_INSTALL)Slog.i(TAG,"startCopy "+mUser+": "+this);

if(++mRetries>MAX_RETRIES){

Slog.w(TAG,"Failed to invoke remote methods on default container service. Giving up");

mHandler.sendEmptyMessage(MCS_GIVE_UP);

handleServiceError();

returnfalse;

}else{

handleStartCopy();

res=true;

}

}catch(RemoteExceptione){

if(DEBUG_INSTALL)Slog.i(TAG,"Posting install MCS_RECONNECT");

mHandler.sendEmptyMessage(MCS_RECONNECT);

res=false;

}

handleReturnCode();

returnres;

}

finalvoidserviceError(){

if(DEBUG_INSTALL)Slog.i(TAG,"serviceError");

handleServiceError();

handleReturnCode();

}

abstractvoidhandleStartCopy()throwsRemoteException;

abstractvoidhandleServiceError();

abstractvoidhandleReturnCode();

}

具体的包的安装、移动以及包大小的测量分别在3个具体子类InstallParams、MoveParams和MeasureParams中实现。

而PackageHandler是Handler的子类,用来负责包相关消息的处理,不同的请求对应不同的命令对象,然后通过命令对象来执行具体操作。

关于Receiver

通过接触Android源码或者其他的一些源码,我们知道有些地方是没有命令接收者(Receiver)这个角色的,这是为什么呢?

个人认为,有的命令接收实现非常简,可以直接用少量的代码来实现,没有必要再增加类的数量。

参考链接:

https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/command/lijunhuayc

http://blog.csdn.net/wwh578867817/article/details/51533263

专栏作者简介()

PleaseCallMeCoder:我是 PleaseCallMeCoder,一个小小的90后程序员。热衷于移动开发,喜欢研究新技术,奔跑在成为大神的路上。

打赏支持作者写出更多好文章,谢谢!

关注「安卓开发精选」

看更多精选安卓技术文章

责任编辑:

android 指令模式,Android 源码中的命令模式相关推荐

  1. 装饰者模式在源码中的应用

    装饰器模式在源码中也应用得非常多,在JDK 中体现最明显的类就是IO 相关的类,如BufferedReader.InputStream.OutputStream,看一下常用的InputStream 的 ...

  2. 【深入设计模式】责任链模式—责任链模式及责任链模式在源码中的应用

    文章目录 1. 责任链模式 1.1 责任链模式简介 1.2 责任链模式结构 1.3 责任链模式示例 2. 责任链模式在源码中的应用 2.1 Servlet 中的责任链模式 2.2 Spring 中的责 ...

  3. Android之在BaseAdapter源码中了解观察者模式

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/76146635 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  4. 设计模式 笔记4 | 简单工厂模式 在源码中的应用 | Calendar 日历 | 源码浅析 | 使用总结 | 建造者模式

    文章目录 一.Calendar 日历类 1.1 内部属性 1.2 设置时间属性值 1.3 获取时间属性 1.4 使用 Calander 计算时间 二.Calender 类中的设计模式 2.1 简单工厂 ...

  5. activiti源码学习之命令模式

    什么是命令模式? 大都很熟悉的设计模式,此问题不在本文介绍范围内. activiti中的命令模式 activiti使用命令模式作为基础开发模型.把每个操作都封装为一个命令,降低代码的耦合度,避免臃肿的 ...

  6. 设计模式--spring源码中使用策略模式(Strategy Pattern)

    转载http://liuxi1024.iteye.com/blog/583145 策略模式(Strategy Pattern)中体现了两个非常基本的面向对象设计的基本原则:封装变化的概念:编程中使用接 ...

  7. 模板模式在源码中的体现

    先来看JDK 中的AbstractList,来看代码: public abstract class AbstractList<E> extends AbstractCollection&l ...

  8. Android直播APP源码中排行榜功能如何实现

    刚进公司的时候,听技术人员说起直播APP源码中的"排行榜"功能,小编最先想到的是学生时期的成绩排行,上榜的沾沾自喜到下次考试,下榜的哭哭啼啼,其实就算上榜也并没有什么实质性的奖励, ...

  9. android告别篇-对于源码我的一些看法

    1.背景         即将结束在sony移动的实习了,在最后的日子有一些伤感有一些感触,对于android这个倾注一年心血的技术有太多话要说.感谢sony移动的各位大牛们给我这个机会,让我彻彻底底 ...

最新文章

  1. 介绍一个打怪升级练习 Python 的网站,寓教于乐~
  2. TCP 网络应用程序开发流程
  3. 成功解决WARNING:tensorflow:From :read_data_sets (from tensorflow.contrib.learn.python.learn.
  4. [PHP 安全] pcc —— PHP 安全配置检测工具
  5. 将一张表的主键(ID)重置为从1开始自增排列
  6. 源代码加密-防泄密解决方案-SDC沙盒
  7. git pull 失败 ,提示:fatal: refusing to merge unrelated histories
  8. Mangos源码分析(15):游戏对象的实现
  9. 每天备份NAS上的www目录到一块单独的硬盘上
  10. matlab中 编程如和隐藏,在matlab中编程(如何实时处理)
  11. mangos服务器架构
  12. MySQL Workbench 使用 (3):数据库备份与恢复
  13. 考查频率最高的吉林八景
  14. 中国口岸年鉴(2001-2015年)
  15. STM8L超低功耗程序编写教学,简单易懂
  16. 计算机流体仿真,计算机流体模拟仿真及其工程应用
  17. 图书信息管理系统(c语言)
  18. 中望3d快捷键命令大全_中望CAD快捷键汇总
  19. es7之Reflect Metadata
  20. 大疆精灵4rtk照片信息读取

热门文章

  1. Gremlin 查询语言
  2. 顺丰丰桥接口开发-java(前篇)
  3. 【测试岗】快来抄模板,3W字41个软件测试超常见实例问题(附带答案)
  4. 怎么删除打印机正在打印的文档?
  5. 如何自动识别爬虫网页的编码
  6. 电路板电压不稳原因分析
  7. GET新技能,再也不怕做数据报告了!
  8. Licode入门学习:DtlsSocket源码分析
  9. unity内的阴影修复。
  10. 12.14补卡,多重背包二进制优化