(点击
上方公众号
,可快速关注)
来源:伯乐在线专栏作者 - 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
()