设计模式之命令模式(Java)

在上一篇文章中,大概总结了单例模式,今天我要说的是另外一种模式即命令模式。废话不多说,直接进入主题。

命令模式的定义:将“请求”封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销操作。

一看到这个定义是不是感觉有点懵圈,那么我们通过下面的例子来看看命令模式到底是怎么回事呢?

命令模式是对命令的封装,它把发出命令的责任(请求者)和执行命令的责任(接收者)分割开,委派给不同的对象。这就是把请求者和接收者解耦合。请求者只负责发起命令,它不需要知道接收者是如何去接收命令,也不需要知道接收者是如何具体的执行命令的。所以命令模式分为四个角色:

命令角色(Command):声明了一个给所有具体命令类的抽象接口。

具体命令角色(ConcreteCommand):定义一个接收者和行为之间的弱耦合;实现execute()方法,负责调用接收者的相应操作。execute()方法通常叫做执行方法。

请求者角色(Invoker):负责调用命令对象执行请求,相关的方法叫做行动方法。

接收者角色(Receiver):负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法。

命令模式的结构可以用下面的图来示意:

这里写图片描述

这个图中说明了上面的四种角色是如何联系在一起的。下面我用代码来说明一下这几个角色是如何在命令模式中工作的。

接收者角色类:

public class Receiver {
    /**
     * 真正执行命令相应的操作
     */
     public void action(){
        System.out.println("执行操作");
     }
}

抽象命令角色类:

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

具体命令角色类:

public class ConcreteCommand implements Command {
    //持有相应的接收者对象
    private Receiver receiver = null;
    /**
     * 构造方法
     */
    public ConcreteCommand(Receiver receiver){
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        //通常会转调接收者对象的相应方法,让接收者来真正执行功能
        receiver.action();
    }

}

请求者角色类:

public class Invoker {
    /**
     * 持有命令对象
     */
    private Command command = null;
    /**
     * 构造方法
     * 这里是构造方法传入命令,也可以单独写个set方法来设置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();
        //创建命令对象,设定它的接收者
        Command command = new ConcreteCommand(receiver);
        //创建请求者,把命令对象设置进去
        Invoker invoker = new Invoker(command);
        //执行方法
        invoker.action();
    }

}

针对上面的模式,我们举个具体的例子其中使用到命令模式。

遥控器(RemoteControl)

电视的遥控器本身有很多按钮,我们假设每个按钮就是一个命令,这样当我们按下一个按钮的时候其实就是再请求一个操作,然后由遥控器接收到命令并去对电视做相应的反应,我们不需要知道遥控器具体做了什么而让电视做相应的反应,我们只负责传达我们想要的命令给遥控器就可以了。

下面我们用代码先来实现遥控器上的两个功能:电源的开关,音量大小。

接收者角色,由遥控器扮演

public class RemoteControl {

    public void turnOn(){
        System.out.println("开机");
    }

    public void turnOff(){
        System.out.println("关机");
    }

    public void soundOn(){
        System.out.println("取消静音");
    }

    public void soundOff(){
        System.out.println("静音");
    }
}

抽象命令角色类

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

具体命令角色类

public class PowerOnCommand implements Command {

    private RemoteControl mRemoteControl;

    public PowerOnCommand(RemoteControl control){
        mRemoteControl = control;
    }

    @Override
    public void execute() {
        mRemoteControl.turnOn();
    }
}
public class PowerOffCommand implements Command {

    private RemoteControl mRemoteControl;

    public PowerOffCommand(RemoteControl control){
        mRemoteControl = control;
    }

    @Override
    public void execute() {
        mRemoteControl.turnOff();
    }
}
public class SoundOnCommand implements Command {

    private RemoteControl mRemoteControl;

    public SoundOnCommand(RemoteControl control){
        mRemoteControl = control;
    }

    @Override
    public void execute() {
        mRemoteControl.soundOn();
    }
}
public class SoundOffCommand implements Command {

    private RemoteControl mRemoteControl;

    public SoundOffCommand(RemoteControl control){
        mRemoteControl = control;
    }

    @Override
    public void execute() {
        mRemoteControl.soundOff();
    }
}

请求者角色,由键盘类扮演

public class Keyboard {

    // 开机
    private Command powerOn;
    // 关机
    private Command powerOff;
    // 音量增
    private Command soundOn;
    // 音量减
    private Command soundOff;

    public void setPowerOn(Command powerOn) {
        this.powerOn = powerOn;
    }

    public void setPowerOff(Command powerOff) {
        this.powerOff= powerOff;
    }

    public void setSoundOn(Command soundOn) {
        this.soundOn= soundOn;
    }

    public void setSoundOff(Command soundOff) {
        this.soundOff = soundOff;
    }

    // 按下开机按钮
    public void turnOn(){
        powerOn.execute();
    }

    // 按下关机按钮
    public void turnOff(){
        powerOff.execute();
    }

    // 按下音量增按钮
    public void soundOn(){
        soundOn.execute();
    }

    // 按下音量减按钮
    public void soundOff(){
        soundOff.execute();
    }
}

现在小红可以拿遥控器操作电视了。代码如下:

public class XiaoHong {

    public static void main(String[]args){
        // 创建接收者
        RemoteControl rc = new RemoteControl();
        // 创建命令对象
        Command powerOnCommand = new PowerOnCommand(rc);
        Command powerOffCommand = new PowerOffCommand(rc);
        Command soundOnCommand = new SoundOnCommand(rc);
        Command soundOffCommand = new SoundOffCommand(rc);
        // 创建请求者
        Keyboard keyboard = new Keyboard();
        keyboard.setPowerOn(powerOnCommand);
        keyboard.setPowerOff(powerOffCommand);
        keyboard.setSoundOn(soundOnCommand);
        keyboard.setSoundOff(soundOffCommand);
        // 测试
        keyboard.turnOn();
        keyboard.turnOff();
        keyboard.soundOn();
        keyboard.soundOff();
    }
}

这样我们就完成了对命令模式的使用。大家是不是对命令模式又有了更进一步的认识了呢?

大家想一想,我们上面都是按个按钮单个操作来执行的,那如果我们按一个按钮能不能执行一组操作呢?答案当然是可以的,而且这种实现方式就叫做宏命令。下面我们来看一下具体的实现。

宏命令

首先我们来定义一个宏命令的接口,该接口有两个方法:remove和add方法,并且这个接口实现了抽象命令接口(Command)

public interface MacroCommand extends Command {
    /**
     * 宏命令聚集的管理方法
     * 可以添加一个成员命令
     */
    public void add(Command cmd);
    /**
     * 宏命令聚集的管理方法
     * 可以删除一个成员命令
     */
    public void remove(Command cmd);
}

具体的宏命令MacroRemoteControlCommand类负责把个别的命令合成宏命令。

public class MacroRemoteControlCommand implements MacroCommand {

    private List<Command> commandList = new ArrayList<Command>();
    /**
     * 宏命令聚集管理方法
     */
    @Override
    public void add(Command cmd) {
        commandList.add(cmd);
    }
    /**
     * 宏命令聚集管理方法
     */
    @Override
    public void remove(Command cmd) {
        commandList.remove(cmd);
    }
    /**
     * 执行方法
     */
    @Override
    public void execute() {
        for(Command cmd : commandList){
            cmd.execute();
        }
    }

}

下面我们假设有一个按钮,先是把音量调大,然后再把音量调小。当然这个按钮实际不存在(哈),我们只是为了测试做个假设。下面我们看怎么去实现一个按钮执行一组操作呢?

public class XiaoHong {

    public static void main(String[]args){
        // 创建接收者
        RemoteControl rc = new RemoteControl();
        // 创建命令对象
        Command soundOnCommand = new SoundOnCommand(rc);
        Command soundOffCommand = new SoundOffCommand(rc);
        // 创建宏命令
        MacroCommand mc = new MacroRemoteControlCommand();

        mc.add(soundOnCommand);
        mc.add(soundOffCommand);

        mc.execute();
    }
}

以上就是命令模式中宏命令的使用。那么到这里相信大家都对命令模式有了初步的认识,那么大家想一想在开发中有没有遇到过命令模式的例子呢?其实有一个就是线程池队列。每个新线程任务当做是一个命令对象放到队列中,然后线程池从队列中取出一个任务执行后,并销毁掉该命令对象。不知道大家还有没有想到别的可使用到命令模式的地方。

综合以上,总结一下命令模式的优点:

(1)命令模式使请求的发起者和接收者完全解耦

(2)命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。

(3)命令模式中的命令对象能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大。

(4)由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。

到这里就对命令模式做了个大概的分析讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值