命令模式 自己理解

命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

以下的这个关键代码放在最上面的原因在于想要更好的理解命令模式,我们需要明确的区分这三部分

关键代码

定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口

我在举例后会给大家区分这个的

介绍

意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
如何解决:通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。
优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。
注意事项:系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

例子

在大学里,辅导员经常会发一些公告或文件在班长群里,然后再由班长转发或执行,很多通知班长仅需要在第一时间转发到班群即可。

在这里插入图片描述
如上图,班长工作大部分是接收辅导员的通知再将通知转给同学,而且大多数情况下不需要关心文件或通知内容(有时候还是需要关心一下的),只需要将通知从班委群转到班级群即可。

​ 对应的命令模式:

在这里插入图片描述
首先客户端需要创建一个命令对象,并将命令对象存储在调用者中,一个命令加载到调用者后,该命令可以被使用后丢弃(如:班长通知五一放假注意安全),也可以重复使用(如:班长发布奖学金公示,需要重复发送确认)。

定义:一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。命令对象将动作和接收者包装进了对象中,这个对象只暴露一个execute()方法,当方法被调用时,接收者会执行命令封装的动作。在使用者看来,并不需要接收者进行了哪些动作,只知道调用一下execute()方法,目的就可以达到。

以上有点抽象 我们用代码来理解

先定义一个命令接口,所有命令实现此接口。

/**
 * 通知
 */
public interface Command {

    void execute();
}

这里简单的定义一个学生群体,作为命令接收者。

/**
 * 一群可爱的学生
 */
public class Students {

    /**
     * 开班会
     */
    public void meeting(){
        System.out.println("教学楼四楼开班会....");
    }

    /**
     * 参加讲座
     */
    public void attendLecture(){
        System.out.println("讲堂群参加讲座....");
    }

    /**
     * 提交材料
     */
    public void submitMaterial(){
        System.out.println("提交材料....");
    }
    //............

}

如果想要实现这个命令就需要将接收者传入命令,这里学生群体还可以扩展修改为其他班级学生,因为一个辅导员所下发通知是相同的。

/**
 * 开会通知
 */
public class MeetCommand implements Command{

    private Students students;

    public MeetCommand(Students students) {
        this.students = students;
    }

    @Override
    public void execute() {
        students.meeting();
    }
}

/**
 * 讲座通知
 */
public class LectureCommand implements Command{

    private Students students;

    public LectureCommand(Students students) {
        this.students = students;
    }

    @Override
    public void execute() {
        students.attendLecture();
    }
}

/**
 * 提交材料通知
 */
public class SubmitMaterialCommand implements Command{

    private Students students;

    public SubmitMaterialCommand(Students students) {
        this.students = students;
    }

    @Override
    public void execute() {
        students.submitMaterial();
    }
}
/**
 * 班长(调用者)
 */
public class Monitor {

    Command command;

    public Monitor() {}

    public void setCommand(Command command) {
        this.command = command;
    }

    // 发布通知
    public void releasingNotice(){
        command.execute();
    }
}
/**
 * 辅导员(客户)
 */
public class Counsellor {

    public static void main(String[] args) {
        // 一班
        Monitor monitorOne = new Monitor();
        Students studentsOne = new Students();
        LectureCommand lectureCommandOne = new LectureCommand(studentsOne);
        // 二班
        Monitor monitorTwo = new Monitor();
        Students studentsTwo = new Students();
        LectureCommand lectureCommandTwo = new LectureCommand(studentsTwo);

        MeetCommand meetCommandTwo = new MeetCommand(studentsTwo);

        // 一班听讲座
        monitorOne.setCommand(lectureCommandOne);
        monitorOne.releasingNotice();

        // 二班听讲座,听完留下来看班会
        monitorTwo.setCommand(lectureCommandTwo);
        monitorTwo.releasingNotice();
        monitorTwo.setCommand(meetCommandTwo);
        monitorTwo.releasingNotice();
    }

}

如上,客户端创建调用者和接收者并把命令传递给调用者,在调用者看来每次得到命令后只需要execute一下就行了。后面无论什么通知,需要实现Command,就可以是一个正常通知。命令对象只提供execute方法,当方法被调用时,接收者就可以进行行为动作,达到请求目的。通过命令对象调用者和接收者之前可以达到间接解耦。

其实这段代码 就是 我刚才最最最开始提及的,一定要区分那三个,才能更好的理解

received 真正的命令执行对象 就是 学生
command 就不用说了 就是command
nvoker 使用命令对象的入口 就是班长

然后你在看下代码看下能更好的区分不

命令模式其他用途

队列:

​ 在Java线程池中,线程的调度基于生产者消费者模式,所以往往在内部会实现一个阻塞队列。可以在一端添加任务,另一端消费任务。同时任务和工作队列之前是解耦的,无论传入什么任务给线程池,工作队列只负责取出任务,执行execute()方法,下一个…。

保留日志:

​ Redis是现在常用的缓存数据库,读写快的一个原因是数据不走磁盘,都是基于内存读写。虽然都是基于内存,但同样提供持久化方案。其中一种叫AOF,相对于RDB来说它是以执行命令逐条追加的方式来持久化的。所以类似Redis,在开发过程中,命令模式也可用于逐条持久化,因为很多应用在进行数据修改后不是立即持久化到磁盘中,而是先放入内存,等待时机写入磁盘,所以数据变更没发快速存储。如果在使用命令模式时,执行完请求将其持久化到磁盘,那么在将来某一时刻即使宕机,只要从磁盘中取出命令逐条执行就行(Redis的AOF也是这样恢复的)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章