【设计模式系列22】命令模式示例及原理分析

设计模式系列总览

设计模式飞机票
三大工厂模式登机入口
策略模式登机入口
委派模式登机入口
模板方法模式登机入口
观察者模式登机入口
单例模式登机入口
原型模式登机入口
代理模式登机入口
装饰者模式登机入口
适配器模式登机入口
建造者模式登机入口
责任链模式登机入口
享元模式登机入口
组合模式登机入口
门面模式登机入口
桥接模式登机入口
中介者模式登机入口
迭代器模式登机入口
状态模式登机入口
解释器模式登机入口
备忘录模式登机入口
命令模式登机入口
访问者模式登机入口
软件设计7大原则和设计模式总结登机入口

前言

本文主要会介绍设计模式中的命令模式及其原理,并会结合示例进行分析。

什么是命令模式

命令模式(Command Pattern)是对命令的封装,每一个命令都是一个操作。首先请求的一方发出请求要求执行一个操作,然后接收的一方收到请求,并执行操作。

命令模式解耦了请求方和接收方,请求方只需要发送命令而不需要关心命令是如何被接收的,不关心命令怎么操作,也不关心命令是否被执行等。

命令模式属于行为型模式。

为什么需要命令模式

在我们的软件开发系统中,行为请求者和真正的执行者通常都是一种紧耦合关系,但是这种情况下,当我们需要修改行为时,如需要撤销或者重做时,只能修改请求者的源代码,而命令模式会通过在行为请求者和执行者之间引入一个抽象接口来将请求者和执行者进行解耦,这样如果需要修改行为时,只需要增加对应行为的命令就可以了,完全不需要修改请求者的源代码。

好了,装逼时刻又到了:Talk is cheap,Show you the code,先看一个非常简单的例子。

命令模式示例

我们就以在XShell中执行Linux操作系统命令为例来写一个示例:

1、首先建立一个真正执行命令类:

package com.zwx.design.pattern.command;

public class LinuxSystem {
    public void cd(){
        System.out.println("已经切换到主目录");
    }

    public void ls(){
        System.out.println("已经列举出当前目录下所有文件");
    }

    public void restart(){
        System.out.println("开始重启系统");
    }
}

2、创建一个命令接口(面向抽象编程,当然在适当的情况下也可以直接进行第3步):

package com.zwx.design.pattern.command;

public interface ICommand {
    void execute();
}

3、上面我们LinuxSystem类中有三个命令,所以接下来为每个命令创建一个类,这些类内部维护了一个LinuxSystem对象,用来命令的真正执行者LinuxSystem打交道。

cd命令类:

package com.zwx.design.pattern.command;

public class CdCommand implements ICommand {
    private LinuxSystem linuxSystem;

    public CdCommand(LinuxSystem linuxSystem) {
        this.linuxSystem = linuxSystem;
    }

    @Override
    public void execute() {
        linuxSystem.cd();
    }
}

ls命令类:

package com.zwx.design.pattern.command;

public class LsCommand implements ICommand {
    private LinuxSystem linuxSystem;

    public LsCommand(LinuxSystem linuxSystem) {
        this.linuxSystem = linuxSystem;
    }

    @Override
    public void execute() {
        linuxSystem.ls();
    }
}

restart命令类:

package com.zwx.design.pattern.command;

public class RestartCommand implements ICommand {
    private LinuxSystem linuxSystem;

    public RestartCommand(LinuxSystem linuxSystem) {
        this.linuxSystem = linuxSystem;
    }

    @Override
    public void execute() {
        this.linuxSystem.restart();
    }
}

4、接下来我们还需要一个用来接收命令的类:

package com.zwx.design.pattern.command;

import org.omg.PortableServer.LIFESPAN_POLICY_ID;

import java.util.ArrayList;
import java.util.List;

public class XshellInvoker {
    private List<ICommand> commandList = new ArrayList<>();

    public XshellInvoker(List<ICommand> commandList) {
        this.commandList = commandList;
    }

    /**
     * 执行指定命令
     * @param command
     */
    public void execute(ICommand command){
        command.execute();
    }

    /**
     * 执行命令宏
     */
    public void executeCdAndLs(){
        for (ICommand command : commandList){
            if (command instanceof LsCommand || command instanceof CdCommand){
                command.execute();
            }
        }
    }

    public void executeAll(){
        for (ICommand command : commandList){
            command.execute();
        }
    }
}

5、最后我们来新建一个测试类:

package com.zwx.design.pattern.command;

import java.util.ArrayList;
import java.util.List;

public class TestCommand {

    public static void main(String[] args) {
        LinuxSystem linuxSystem = new LinuxSystem();
        List<ICommand> commandList = new ArrayList<>();
        commandList.add(new CdCommand(linuxSystem));
        commandList.add(new LsCommand(linuxSystem));
        commandList.add(new RestartCommand(linuxSystem));

        XshellInvoker xshellInvoker = new XshellInvoker(commandList);

        xshellInvoker.execute(new LsCommand(linuxSystem));//执行指定命令
        System.out.println("------------------------");
        xshellInvoker.executeCdAndLs();//指定特定命令宏
        System.out.println("------------------------");
        xshellInvoker.executeAll();//执行全部命令
    }
}

运行结果如下:

已经列举出当前目录下所有文件
------------------------
已经切换到主目录
已经列举出当前目录下所有文件
------------------------
已经切换到主目录
已经列举出当前目录下所有文件
开始重启系统

上面就是一个命令模式的标准写法,可以看到,因为控制器已经与命令执行者实现了解耦,后面如果想要继续扩展新的命令,那么再增加一个命令类就行了。

命令模式角色

从上面示例中,我们可以得出迭代器模式主要有4个角色:

  • 接收者角色(Receiver):负责具体实施或执行一个请求(如示例中的LinuxSystem)。
  • 命令角色(Command):定义需要执行的所有命令行为(如示例中的ICommand)。
  • 具体命令角色(ConcreteCommand):该类内部维护一个接收者(Receiver),收到执行命令请求后会调用Receiver对应方法(如示例中的CdCommand,LsCommand,RestartCommand)。
  • 请求者角色(Invoker):接收客户端的命令并调用相应的命令(如示例中的)。

命令模式应用场景

其实命令模式的思想也是通过一个中间者来解耦命令的请求者和实现者。主要适用于以下场景:

  • 1、现实语义中存在具备“命令”的操作,如:dos命令,shell命令。
  • 2、请求调用者和请求的接收者需要解耦,使得调用者和接收者不直接互相调用。
  • 3、需要抽象出等待执行的行为,比如撤销和和恢复等操作。
  • 4、需要支持命令宏(即命令组合)操作。

命令模式优缺点

优点:

  • 1、通过引入中间件(抽象接口)解耦了请求与实现。
  • 2、扩展方便,增加新命令直接增加一个对象即可。
  • 3、支持命令的组合操作,比较灵活方便。

缺点

  • 1、如果命令过多的时候,会多出非常庞大的对象。

总结

本文主要介绍了命令模式的基本使用,并通过一个简单的示例来帮助更好的理解命令模式,最后也简单概括了使用命令模式的优缺点。

请关注我,和孤狼一起学习进步

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

双子孤狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值