专栏名称: 安卓开发精选
伯乐在线旗下账号,分享安卓应用相关内容,包括:安卓应用开发、设计和动态等。
目录
相关文章推荐
开发者全社区  ·  DB总的瓜 ·  昨天  
开发者全社区  ·  字节CEO全员会反思DeepSeek影响 ·  昨天  
开发者全社区  ·  央企入职半年发5W多年终奖 ·  2 天前  
开发者全社区  ·  突发:DeepSeek被封杀 ·  2 天前  
51好读  ›  专栏  ›  安卓开发精选

Android 源码中的命令模式

安卓开发精选  · 公众号  · android  · 2016-10-11 08:16

正文

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


来源:伯乐在线专栏作者 - 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负责组装各个部分。


命令角色


public interface Command {

public void execute ();

public void undo ();

public void redo ();

}


命令接收者


public class Cook {

//烤乳猪的方法

public void cookPig (){

}

//烧花鸭的方法

public void cookDuck (){

}

}


具体命令角色


//做烤乳猪的命令

public class PigCook implements Command {

private Cook mCook ;

public PigCook ( Cook cook ) {

mCook = cook ;

}

@ Override

public void execute () {

mCook . cookPig ();

}

@ Override

public void undo () {

}

@ Override

public void redo () {

}

}

//做烧花鸭的命令

public class DuckCook implements Command {

private Cook mCook ;

public DuckCook ( Cook cook ) {

mCook = cook ;

}

@ Override

public void execute () {

mCook . cookDuck ();

}

@ Override

public void undo () {

}

@ Override

public void redo () {

}

}


调用者角色


public class Waiter {

private Command pig ;

private Command duck ;

public void setCommandPig ( Command pig ) {

this . pig = pig ;

}

public void setCommandDuck ( Command duck ) {

this . duck = duck ;

}

/**

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

*/

public void invoke ( int args ) {

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

if ( args == 0 ){

pig . execute ();

} else if ( args == 1 ){

duck . execute ();

}

}

}


客户角色


public class Client {

/**

* 组装操作

*/

public void assembleAction () {

//创建一个命令接收者

Cook mCook = new Cook ();

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

Command pig = new PigCook ( mCook );

Command duck = new DuckCook ( mCook );

Waiter mWaiter = new Waiter (); //创建一个命令调用者

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

mWaiter . setCommandPig ( pig );

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

mWaiter . setCommandDuck ( duck );

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

mWaiter . invoke ( 0 );

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

mWaiter . invoke ( 1 );

}

}


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


public class Client {

/**

* 组装操作

*/

public void assembleAction () {

//创建一个命令接收者

Cook mCook = new Cook ();

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

mCook . cookPig ();

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

mCook . cookDuck ();

}

}


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


总结


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

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

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

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


Android源码中的命令模式


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



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


private abstract class HandlerParams {

private static final int MAX_RETRIES = 4 ;

/**

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

* error.

*/

private int mRetries = 0 ;

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

private final UserHandle mUser ;

String traceMethod ;

int traceCookie ;

HandlerParams ( UserHandle user ) {

mUser = user ;

}

UserHandle getUser () {

return mUser ;

}

HandlerParams setTraceMethod ( String traceMethod ) {

this . traceMethod = traceMethod ;

return this ;

}

HandlerParams setTraceCookie ( int traceCookie ) {

this . traceCookie = traceCookie ;

return this ;

}

final boolean startCopy ()







请到「今天看啥」查看全文