设计模式之命令者模式

命令模式.png

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战java

欢迎来到今天的学习,今天咱们一块儿来学习下极其强悍的一种模式----命令者模式。我命令你把这篇文章看完,哈,开个玩笑,多唠叨几句,我本月将会对java的设计模式精讲,欢迎点击头像,关注个人专栏,我会持续更新,加油!程序员

系列文章:shell

设计模式之单例模式设计模式

设计模式之工厂模式bash

设计模式之建造者模式markdown

设计模式之代理模式编辑器

设计模式之访问者模式ide

设计模式之适配器模式函数

...持续更新中post

话很少说,进入正题

命令者模式

我我的认为该模式在实际场景中运用不少,可能你没有发现,等我讲完以后你就会以为,你也能够改造一些东西。咱们能够理解为事件类型的通知型模式,好比我经过一个事件去完成什么操做。那命令就是带着发布命令内容去执行。

命令模式的原始定义是:将一个请求封装为一个对象,从而让咱们能够参数化具备不一样请求、队列或日志请求的其余对象,并支持可撤销的操做

注意:从定义中咱们能够得出,命令模式是为了将一组操做封装在对象中而设计的,通俗讲,就是为了将函数方法封装为对象以方便传输。好比像Java 从 8 之后支持将函数做为参数传递(咱们都应该体验过stream,lambda表达式等操做。) 这个就是命令模式的核心理解吧。

看下方图:

image.png

从图中咱们能够看到五个角色(注意看图中箭头指向):

  • 抽象命令类(Command):用于声明须要作的操做有哪些,当你看到代码中有Command的时候,能够知道命令模式有参与了

  • 具体命令类(Command一、2等):实现 Command 接口,其中存储一个接收者类,并在 execute 调用具体命令时,委托给接收者来执行具体的方法

  • 调用者(Invoker),客户端经过与调用者交互来操做不一样的命令对象。

  • 抽象接收者(Receiver),声明须要执行的命令操做,同时提供给客户端使用(看图中箭头)

  • 具体接收者(Receiver一、2等):实现抽象接收者,用于接收命令并执行真实的代码逻辑。(有多少命令就有多少执行者

命令模式的核心关键点就在于围绕着命令来展开,经过抽象不一样的命令,并封装到对象,让不一样的接收者针对同一个命令都能作出相应的操做。

接下来咱们经过一组场景和代码来深刻理解下(再看下上方类图)

代码示例

对于命令模式的使用场景,一个经典的类比例子就是 Shell 脚本。若是你熟悉 Shell 脚本的话,就会发现一个 Shell 脚本其实就是这里的 Invoker 调用者,脚本里各式各样的 ps、cat、sed 等命令就是 Command,而 bash shell 或 z shell 就是做为接收者来具体实现执行命令的。

固然,命令模式并不只限于操做系统的命令,在实际的业务开发中,多是对应的一组复杂的代码调用逻辑,好比,触发数据统计、日志记录、链路跟踪等。

咱们设想这样一种场景,程序员都得在电脑上记录文档,咱们有的用有道云笔记,印象笔记,还有的用mac电脑的备忘录,这三者都是你打开,而后边写边同步保存,最后你关闭该软件这里面涉及到三种命令:打开,同步保存,关闭

咱们先来建立一个抽象命令类 Command,其中定义一个无返回的方法 execute。

//命令接口
public interface Command {
    void execute();
}
复制代码

再来依次实现打开(Open)、同步保存(syncSave)、关闭(Close)三个操做,每一个操做中都存有一个 Editor(抽象接收者类),在实现方法 execute 时,会调用 Editor 对应的 open、syncSave 和 close 方法。

//具体命令类


public class Open implements Command {
    private Editor editor;
    public Open(Editor editor) {
        this.editor = editor;
    }
    @Override
    public void execute() {
        editor.open();
    }
}
public class SyncSave implements Command {
    private Editor editor;
    public Save(Editor editor) {
        this.editor = editor;
    }
    @Override
    public void execute() {
        editor.save();
    }
}
public class Close implements Command{
    private Editor editor;
    public Close(Editor editor) {
        this.editor = editor;
    }
    @Override
    public void execute() {
        editor.close();
    }
}
复制代码

而后,咱们须要定义 Editor 的三个操做方法:打开、同步保存和关闭。

public interface Editor {
    void open();
    void syncSave();
    void close();
}
复制代码

接着咱们再来实现支持 有道云 的编辑器 YouDaoEditor(具体接收者),分别实现打开、同步保存和关闭三个方法,这里具体只是打印了三种不一样的操做。

public class YouDaoEditor implements Editor {

    @Override
    public void open() {
        System.out.println("-> YouDaoEditor 执行 open 操做");
    }
    @Override
    public void syncSave() {
        System.out.println("-> YouDaoEditor 执行 syncSave 操做");
    }
    @Override
    public void close() {
        System.out.println("-> YouDaoEditor 执行 close 操做");
    }
}
复制代码

一样,再实现支持 印象笔记 的编辑器 YinXiangEditor,功能和 YouDaoEditor 相同。

public class YinXiangEditor implements Editor {
    @Override
    public void open() {
        System.out.println("-> YinXiangEditor 执行 open 操做");
    }
    @Override
    public void syncSave() {
        System.out.println("-> YinXiangEditor 执行 syncSave 操做");
    }
    @Override
    public void close() {
        System.out.println("-> YinXiangEditor 执行 close 操做");
    }
}
复制代码

最后,咱们调用一下

//定义一个切入口
public class RunCommond {
    private final List<Command> commands;
    public RunCommond() {
        commands = new ArrayList<>();
    }
    public void setCommand(Command command) {
        commands.add(command);
    }
    public void run() {
        commands.forEach(Command::execute);
    }
}



//调用
public class Client {
    public static void main(String[] args) {
        YouDaoEditor youdaoEditor = new YouDaoEditor();
        YinXiangEditor yinxiangEditor = new YinXiangEditor();
        Open youdaoOpen = new Open(youdaoEditor);
        SyncSave yinxiangSyncSave = new SyncSave(yinxiangEditor);
        RunCommond runCommond = new RunCommond();
        //暂时写着两个命令,其余同样。
        runCommond.setCommand(youdaoOpen);
        runCommond.setCommand(yinxiangSyncSave);
        runCommond.run();
    }
}

//控制台输出
-> YouDaoEditor 执行 open 操做
-> YinXiangEditor 执行 SyncSave 操做
复制代码

OK,到这里今天的学习就完成了。

总结

命令模式的使用场景也很是局限,只能针对命令顺序执行的场景,而对于须要多种组合的场景来讲,命令模式并非很适合。

命令模式将一个或一组命令封装为一个对象,从而可以将函数方法做为参数进行传输,同时还可以解耦客户端和服务端的直接耦合,适用场景有:作简单的请求排队,记录请求日志,以及支持可撤销的操做。

简单来讲,命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分离开。

另外给你们看段话,我以为挺好,也能够放到本文总结,固然也适用于全部的模式,我看过一本书是付政委的《重学设计模式》里面有这样一段话,与你们共勉之。

命令模式分为命令、实现者和调用者。而这三块内容的拆分也是选择场景的关键因素,通过拆分,可让逻辑具有单一职责的性质,便于扩展。与if语句相比,这种实现方式下降了耦合性,也方便其余命令和实现的扩展。但这种设计模式也带来了一些问题,在各类命令与实现者的组合下,会扩展出不少的实现类,须要管理。学习设计模式必定要勤加练习,哪怕最开始只是模仿实现,屡次练习后再去找一些能够优化的场景,并逐步运用到本身的开发中,提高本身对代码的设计感受,让代码结构更加清晰,易于扩展。

弦外之音

感谢你的阅读,若是你感受学到了东西,麻烦您点赞,关注。

我已经将本章收录在专题里,点击下方专题,关注专栏,我会天天发表干货,本月我会持续输入设计模式。

加油! 咱们下期再见!

相关文章
相关标签/搜索