您的位置:首页 > 移动开发 > Android开发

十、命令设计模式

2016-04-29 20:26 519 查看

1. 命令设计模式介绍

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

2. 命令模式使用场景

整个调用过程比较复杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。

调用前后需要对调用参数进行某些处理。

调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等等。

3. 命令模式的UML类图



4. 命令模式的简单实现

通用模式代码:

接收者类:

public class Receiver {

/**
* 真正执行具体命令逻辑的方法
*/
public void action() {
System.out.println("执行具体操作");
}
}


抽象命令接口:

public interface Command {

/**
* 执行具体操作的命令
*/
void execute();
}


具体命令类:

public class ConcreteCommand implements Command {
private Receiver receiver;

public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void execute() {
//调用接收者相关方法来执行具体逻辑
receiver.action();
}
}


请求者类:

public class Invoker {
private Command command;//只有一个对相应命令对象引用

public Invoker(Command command) {
this.command = command;
}

public void action() {
//调用具体命令对象的相关方法,执行具体命令
command.execute();
}
}


客户端类:

public class Client {
public static void main(String[] args) {

//构造一个接收者对象
Receiver receiver = new Receiver();

//根据接收者对象构造一个命令对象
ConcreteCommand concreteCommand = new ConcreteCommand(receiver);

//根据具体的对象构造请求者对象。
Invoker invoker = new Invoker(concreteCommand);

//执行请求方法
invoker.action();
}
}


下面简单说下以上各个类的作用:

Receiver: 接收者角色, 其实就是执行具体逻辑的角色。

Command: 命令角色, 定义所有具体命令的抽象接口。

ConcreteCommand: 具体命令角色, 该类实现了Command接口。在execute方法中调用接收者角色的相关方法,在接收者和命令执行的具体行为之间加以弱耦合。

Invoker: 请求角色。该类的职责就是调用命令对象执行具体的请求。

Client: 客户端角色。

以上是一个通用模式代码,下面说一个具体情景。

比如我们日常生活中的开关灯泡操作。

(1)首先得有一个灯泡类:Bulb

public class Bulb {

//开灯
public void lightUp() {
System.out.println("开灯");
}

//关灯
public void lightOff() {
System.out.println("关灯");
}
}


Bulb类是整个命令模式中唯一处理具体代码逻辑的地方,其他的类都是直接或者间接地调用该类的方法,这个就是接收者的角色,处理具体的逻辑。

(2) 定义命令抽象者,定义执行方法。

public interface Command {
/**
* 命令执行方法
*/
void execute();
}


(3) 具体命令者,开关灯的命令

开灯:

public class LightUpCommand implements Command {

//持有一个灯泡的引用
private Bulb bulb;

public LightUpCommand(Bulb bulb) {
this.bulb = bulb;
}

@Override
public void execute() {
bulb.lightUp();
}
}


关灯:

public class LightOffCommand implements Command {

//持有一个灯泡的引用
private Bulb bulb;

public LightOffCommand(Bulb bulb) {
this.bulb = bulb;
}

@Override
public void execute() {
bulb.lightOff();
}
}


(4) 命令由开关发起

public class Switch {
private LightUpCommand lightUpCommand;
private LightOffCommand lightOffCommand;

/**
* 设置开灯命令
*
* @param lightUpCommand
*/
public void setLightUpCommand(LightUpCommand lightUpCommand) {
this.lightUpCommand = lightUpCommand;
}

/**
* 设置关灯命令
*
* @param lightOffCommand
*/
public void setLightOffCommand(LightOffCommand lightOffCommand) {
this.lightOffCommand = lightOffCommand;
}

/**
* 开灯
*/
public void lightUp() {
lightUpCommand.execute();
}

/**
* 关灯
*/
public void lightOff() {
lightOffCommand.execute();
}
}


(5) 客户端调用:

public class Person {
public static void main(String[] args) {
//1.首先得有一个灯泡
Bulb bulb = new Bulb();
//2.构造两种命令:开、关灯
LightUpCommand lightUpCommand = new LightUpCommand(bulb);
LightOffCommand lightOffCommand = new LightOffCommand(bulb);
//3.设置开关不同状态的执行命令
Switch mSwitch = new Switch();
mSwitch.setLightUpCommand(lightUpCommand);
mSwitch.setLightOffCommand(lightOffCommand);
//4.开关操作
mSwitch.lightUp();
mSwitch.lightOff();
}
}


其实上面调用了这么多,可以用以下代码来代替:

bulb.lightUp();
bulb.lightOff();


直接调用Bulb对象即可。

既然直接调用即可,为什么要调用这么复杂呢?

因为设计模式模式有一条重要的原则: 对修改关闭,对扩展开放。如果我们直接操作对象,就可能会不小心修改错误,而这些错误我们恰好没有发现。此时就用到了命令模式,间接调用。

另一个好处是,在Switch类中,我们可以使用一种存储结构来存储执行过的命令对象,作为日志输出。

5. 命令模式在Android源码中

(1)Android 的事件机制中底层逻辑对事件的转发处理

Android 的事件机制中底层逻辑对事件的转发处理,Android的每一种事件在屏幕上产生后,都经由底层逻辑将其转换为一个NotifyArgs对象,相当于一个命令抽象者。

NotifyKeyArgs等子类则相当于一个具体命令者。

请求者角色由InputDispatcher承担,具体的事件名来者将事件转发给InputDispatcher,并由其来封装具体的事件操作。

(2) Android在对应用程序包管理的部分也有对命令模式应用的体现

PackageManagerService是Android系统的Service之一,主要功能在于实现对应用包的解析,管理,卸载。对于这三个操作,分别封装为HandlerParams的3个具体子类InstallParams、MoveParams和MeasureParams。结合命令模式,HandlerParams可以看成是一个抽象命令模式。

6. 命令模式在Android开发中

在Android开发中,当我们开启一个线程时,我们经常写到如下的代码:

new Thread(new Runnable() {
@Override
public void run() {

}
}).start();


其实这就是一个典型的命令模式。如果我们写成如下的形式:

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

/*new Thread(new Runnable() { @Override public void run() { } }).start();*/

//1.构造一个接收者对象
Receiver receiver = new Receiver();
//2. 根据接收者对象构造ChildRunnable
ChildRunnable childRunnable = new ChildRunnable(receiver);
//3.根据ChildRunnable构造一个子线程对象
Thread childThread = new Thread(childRunnable);
//4.执行请求方法
childThread.start();
}

//ChildRunnable是命令角色,Runable是命令角色
class ChildRunnable implements Runnable {
private Receiver receiver;

public ChildRunnable(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void run() {
receiver.receive();
}
}

//接收者
class Receiver {
public void receive() {
System.out.println("接收到了");
}
}
}


由上述我们不难发现:

Receiver是接收者角色

Runnable 是命令角色

ChildRunnable 是具体命令角色

Thread是请求角色

MainActivity是客户端角色

7. 总结

优点:

命令模式的封装性很好。每个命令都被封装起来了,对于客户端来说,想要什么功能,直接去调用即可,而无需知道命令具体是怎么执行的。

命令模式的扩展性很好。比如我们想要实现一个剪切的功能,目前已有的功能有复制删除。所以我们只需要在命令类,对接收者类中的“复制”和“删除”功能进行封装,就完成了“剪切”的功能,而无需在接收者类增加新的代码。

缺点:

如果是简单的命令,实现起来很简单的恶化,使用命令模式就会显得繁杂,需要一个命令类去封装。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息