设计模式-命令模式简介

命令模式

1 命令模式简介

1.1 什么是命令模式

命令模式是一种行为设计模式,它可将请求转换成一个包含与请求相关的所有信息的独立对象。

该转化让你能根据不同的请求将方法参数化、延迟请求执行或将其放入队列中,且能实现可撤销操作。

1.2 命令模式的几个组成部分

  • Command

    一个具体命令的抽象接口。

  • ConcreteCommand
    命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。

  • Receiver

    作为具体命令的最终接收者。几乎任何对象都可以作为接收者,命令的细节由Receiver来实现。

  • Invoker

    携带命令执行 action请求。它不负责创建命令对象,它通常会通过构造函数从客户端处获得预先生成的命令。

  • Client

    发起具体的命令交给Invoker去执行。

1.3 命令模式在开发中适用的情况:

​ 1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
​ 2.系统需要在不同的时间指定请求、将请求排队和执行请求。
​ 3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
​ 4.系统需要将一组操作组合在一起,即支持宏命令。

2 给定一个需求

​ 需求: 对于不同的智能家电设备,这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制,我们希望只要一个App就可以控制全部智能家电. 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,在这里,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品.

2.1 一般的做法

1.首先我们需要定义几个接收请求的类,也就是我们需求中的家电类

我们这里定义智能电视以及电灯两个类,然后在里面定义一些响应请求的方法

//电灯类
public class Light {
    public void  turnOn(){
        System.out.println("电灯打开成功");
    }
    public void  turnOff(){
        System.out.println("电灯关闭成功");
    }
}
//智能电视类
public class Tv {
    public void turnOn(){
        System.out.println("智能电视开机");
    }
    public void turnOff(){
        System.out.println("智能电视开机");
    }
}
2.之后我们需要定义一个发布请求的手机类,里面需要有一些特点的请求方法,而且手机类需要持有他能控制的家电类的对象
//手机类
public class Phone {
    Tv tv;
    Light light;
    public Phone(Tv tv,Light light){
        this.tv=tv;
        this.light=light;
    }
    public void openTv(){
        this.tv.turnOn();
    }
    public void closeTv(){
        this.tv.turnOff();
    }
    public void openLight(){
        this.light.turnOn();
    }
    public void closeLight(){
        this.light.turnOff();
    }
}
3.然后我们可以进行测试
//测试类
public class Test {
    public static void main(String[] args) {
        Light light=new Light();
        Tv tv=new Tv();
        Phone phone=new Phone(tv,light);
        phone.openLight();
        phone.openTv();
    }
}

在传统的模式下,我们的控制器和设备是强相关的,因为遥控器的代码在具体使用、调用的过程中用的功能是设备里面具体的执行的函数,所以如果一方变动另一方也要变动。

而我们项目开发过程,是对类的的扩展是开放的,但是对代码的修改是关闭的,也就是说,在扩展的过程中不能给老代码带来新的bug

2.2 命令模式

而命令模式的做法是将请求封装称为一个命令对象,调用者只要命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说 :“请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用。

在这里插入图片描述

1.首先一样,我们先创建Receiver类,也就是家电类

​ 我们这里使用一个Recevier接口,然后用家电类实现Recevier接口,方便管理。

//命令接收器接口
public interface Receiver {
    //开机
    public void trunOn();
    //关机
    public void trunOff();
}

​ 智能电视类实现Recevier接口

public class Tv implements Receiver{
    @Override
    public void trunOn() {
        System.out.println("智能电视开机");
    }
    @Override
    public void trunOff() {
        System.out.println("智能电视关机");
    }
}

电灯类实现Recevier接口

public class Light implements Receiver{
    @Override
    public void trunOn() {
        System.out.println("开灯");
    }
    @Override
    public void trunOff() {
        System.out.println("关灯");
    }
}
2.然后我们再创建一个Command接口
//命令接口
public interface Command {
    /**
     * 执行操作
     */
    public void execute();
    /**
     * 操作回退
     */
    public void fallback();
}

3.之后再创建具体的请求的Command类

//关灯的命令类
public class LightOffCommand implements Command{
    //关灯命令的接受者   也就是电灯
    private Receiver receiver;
    public LightOffCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.trunOff();
    }
    @Override
    public void fallback() {
        receiver.trunOn();
    }
}
//开灯的命令类
public class LightOnCommand implements Command{
    //开灯命令的接受者   也就是电灯
    private Receiver receiver;
    public LightOnCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        receiver.trunOn();
    }
    @Override
    public void fallback() {
        receiver.trunOff();
    }
}
//电视开机的命令类
public class TvOffCommand implements Command{
    //电视关机命令的接受者   也就是电视
    public Receiver receiver;
    public TvOffCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        this.receiver.trunOff();
    }
    @Override
    public void fallback() {//也可以变成一个回退的函数
        this.receiver.trunOn();
    }
}
//电视关机的命令类
public class TvOnCommand implements  Command{
    //电视开机命令的接受者   也就是电视
    public Receiver receiver;
    public TvOnCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    @Override
    public void execute() {
        this.receiver.trunOn();
    }
    @Override
    public void fallback() {//也可以变成一个回退的函数
        this.receiver.trunOff();
    }
}

4.然后我们创建Invoker,在这里我们可以装在各种不同的Command类,载入命令类进入对列,之后可以进行命令的执行或者撤销/回退

//相等于一个手机类
public class Invoker {
    // 打开 按钮的命令数组
    private Command[] onCommands;
    // 执行撤销的命令
    private Command undoCommand;
    /**
     * 构造器,完成对按钮初始化
     */
    public Invoker() {
        onCommands = new Command[5];
        for (int i = 0;i < 5; i++) {
            onCommands[i] = new NullCommand();//初始化时,对按钮初始化了空命令,这样从数组中取命令时,就不用判断是否为空了
        }
    }
    /**
     * 给我们的按钮设置你需要的命令
     * @param no
     * @param onCommand
     * @param //offCommand
     */
    public void setCommand(int no, Command onCommand) {
        onCommands[no] = onCommand;
    }
    /**
     * 按下开按钮
     * @param no
     */
    public void onCommand(int no) {
        // 找到你按下的开的按钮,并调用对应的方法
        onCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = onCommands[no];
    }
    /**
     * 按下撤销按钮
     */
    public void fallback() {
        undoCommand.fallback();
    }
}

5.最后我们可以建立一个Client类,给Invoke注入具体的Command类,以及你需要的Receiver类

//创建一个ConcreteCommand对象并指定他的Receiver对象
public class Client {
    public static void main(String[] args) {
        // 命令接收者Receiver
        Tv myTv = new Tv();
        // 开机命令ConcreteCommond
        Command Tvon = new TvOnCommand(myTv);
        // 关机命令ConcreteCommond
        Command Tvoff = new TvOffCommand(myTv);
        Light light=new Light();
        Command Lighton = new LightOnCommand(light);
        // 关机命令ConcreteCommond
        Command Lightoff = new LightOffCommand(light);
        Invoker invoke=new Invoker();
        //命令可以装载入invoke,用的时候调用
        //可以使用回调机制
        //放入命令队列,慢慢处理(抢购)
        invoke.setCommand(0,Tvon);
        //可以实现命令的组合
        invoke.onCommand(0);
        invoke.fallback();
    }
}

3.总结一下命令模式优缺点

3.1 优点

  • 解耦。命令发起者和接收者不存在直接的引用,二者独立变化。
  • 易扩展。增加新命令,不需要修改客户端代码。
  • 易组合。可以通过命令与命令组合,创建更复杂的命令,比如一个批处理命令。
  • 可以撤销和恢复。通过对命令的记录,可以撤销和恢复之前的命令。

3.2 缺点

  • 类膨胀。一个具体的命令要对应一个命令类,会导致具体命令类变多。

借鉴:

https://blog.csdn.net/jason0539/article/details/45110355?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param

项目文件已上传到git:

https://github.com/coder-oyz/command_mode.git

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值