十、命令设计模式

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("接收到了");
            }
        }
}

由上述我们不难发现:

  1. Receiver是接收者角色
  2. Runnable 是命令角色
  3. ChildRunnable 是具体命令角色
  4. Thread是请求角色
  5. MainActivity是客户端角色

7. 总结

  • 优点:

    • 命令模式的封装性很好。每个命令都被封装起来了,对于客户端来说,想要什么功能,直接去调用即可,而无需知道命令具体是怎么执行的。
    • 命令模式的扩展性很好。比如我们想要实现一个剪切的功能,目前已有的功能有复制删除。所以我们只需要在命令类,对接收者类中的“复制”和“删除”功能进行封装,就完成了“剪切”的功能,而无需在接收者类增加新的代码。
  • 缺点:

    • 如果是简单的命令,实现起来很简单的恶化,使用命令模式就会显得繁杂,需要一个命令类去封装。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值