命令模式&中介者模式

沙海 2021年7月20日01:10:36Java评论49字数 8712阅读29分2秒阅读模式
摘要

智能摘要

智能摘要文章源自JAVA秀-https://www.javaxiu.com/38180.html

最近在跟大家分享设计模式系列的文章有学妹问我,命令模式、策略模式、工厂模式它们分别有啥区别?以上就是简单的代码实现了,通过Invoker(皇帝)的选择可以让Receiver(公公)确定去执行什么命令。所以针对设计模式,其实我理解的还是只说明了一个问题,不同的设计模式都是为了针对处理不同的场景,不同业务场景有不同的写法。中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。文章源自JAVA秀-https://www.javaxiu.com/38180.html

原文约 3561 | 图片 4 | 建议阅读 8 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式&中介者模式

原创 三太子敖丙 三太子敖丙 文章源自JAVA秀-https://www.javaxiu.com/38180.html

收录于话题文章源自JAVA秀-https://www.javaxiu.com/38180.html

#设计模式文章源自JAVA秀-https://www.javaxiu.com/38180.html

13个文章源自JAVA秀-https://www.javaxiu.com/38180.html

最近在跟大家分享设计模式系列的文章有学妹问我,命令模式、策略模式、工厂模式 它们分别有啥区别?看代码的实现上感觉没啥区别呀?文章源自JAVA秀-https://www.javaxiu.com/38180.html

我说:文章可能有点长,你忍一下文章源自JAVA秀-https://www.javaxiu.com/38180.html

之前已经跟大家分享了策略模式以及工厂模式感兴趣的同学可以再去复习一下,今天我们就先重点分析一下命令模式然后再来看看它们的区别是啥?文章源自JAVA秀-https://www.javaxiu.com/38180.html

往期回顾:文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式&中介者模式文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式

定义文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 提供一个统一的方法来封装命令,通过参数条件来判断选择执行什么命令动作。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 允许将每一个命令存储在一个队列中。文章源自JAVA秀-https://www.javaxiu.com/38180.html

整体结构图如下:文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式&中介者模式文章源自JAVA秀-https://www.javaxiu.com/38180.html

结构图中重要角色解释:文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Command(命令类):定义命令的抽象封装类。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • ConcreteCommand(具体命令类):对Command类进行实现,说白了就是具体的命令的实际实现类。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Receiver(接收者):执行命令关联的操作类。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Invoker(调用者):触发命令类,即外部操作事件触发执行。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Client(客户端):实例化具体命令对象,及接收者的实际类。文章源自JAVA秀-https://www.javaxiu.com/38180.html

整个结构其实看上去还是比较难理解的,但是既然开始在学设计模式了,那肯定每种设计模式都要有了解,来提升自己的知识面文章源自JAVA秀-https://www.javaxiu.com/38180.html

为了加深理解,我还是举一个好理解的例子:文章源自JAVA秀-https://www.javaxiu.com/38180.html

大家对中国古代君主制度肯定很熟悉。皇帝可以针对手底下服侍的公公让她们可以收取或者发放奏折。那其实这里面我个人感觉就可以体现命令模式。文章源自JAVA秀-https://www.javaxiu.com/38180.html

公公 相当于命令模式的接受者(Receiver),执行皇帝的命令,收取早朝奏折(ConcreteCommand) 还是颁布圣旨(ConcreteCommand)文章源自JAVA秀-https://www.javaxiu.com/38180.html

皇帝 相当于命令模式的调用者(Invoker)文章源自JAVA秀-https://www.javaxiu.com/38180.html

老规矩,例子说完,看看代码吧文章源自JAVA秀-https://www.javaxiu.com/38180.html

// 定义 命令类public interface Command {    // 执行的方法    void execute();}// 定义接收者-公公的角色public class Receiver {    public void Charge(){        System.out.println("收取奏折");    }    public void Issue(){        System.out.println("颁布圣旨");    }}//具体命令类one,收取奏折命令public class ConcreteCommandOne implements Command {    // 接受者,这里可以理解为公公    private Receiver receiver;    public ConcreteCommandOne(Receiver receiver) {        this.receiver = receiver;    }    @Override    public void execute() {        // 收取奏折        receiver.Charge();    }}// 具体命令类two,颁布圣旨public class ConcreteCommandTwo implements Command {    // 接受者,这里可以理解为公公    private Receiver receiver;    public ConcreteCommandTwo(Receiver receiver) {        this.receiver = receiver;    }    @Override    public void execute() {        // 颁布圣旨        receiver.Issue();    }}// 调用者,皇帝public class Invoker {      private Command command;    public Invoker(Command command) {        this.command = command;    }    // 本次需要执行的命令    public void action() {        command.execute();    }} // 测试demo    public static void main(String[] args) {        // 实例化一个公公 接收者        Receiver receiver =new Receiver();        // 公公 当前能有接收到的几种命令        Command commandOne = new ConcreteCommandOne(receiver);        Command commandTwo = new ConcreteCommandTwo(receiver);        // 皇帝 发号命令 触发执行方法        Invoker invoker =new Invoker(commandOne);        invoker.action();        // result: 收取奏折        Invoker invokerTwo =new Invoker(commandTwo);        invokerTwo.action();        // result:颁布圣旨    }

以上就是简单的代码实现了,通过Invoker(皇帝)的选择可以让Receiver(公公)确定去执行什么命令。这其实就是命令模式的一种简单体现。文章源自JAVA秀-https://www.javaxiu.com/38180.html

细心的同学不知道有没有发现一个问题,在定义里面文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 允许将每一个命令存储在一个队列中。文章源自JAVA秀-https://www.javaxiu.com/38180.html

我们这里是没有体现队列的,其实这个实现也很简单。在main方法中添加一个队列就可以了文章源自JAVA秀-https://www.javaxiu.com/38180.html

    public static void main(String[] args) {        // 实例化一个公公 接收者        Receiver receiver = new Receiver();        // 公公 当前能有接收到的几种命令        Command commandOne = new ConcreteCommandOne(receiver);        Command commandTwo = new ConcreteCommandTwo(receiver);    // 存储命令        Queue<Command> queue = new LinkedList<>();        queue.add(commandOne);        queue.add(commandTwo);    // 批量执行        for (Command command : queue) {            Invoker invoker = new Invoker(command);            invoker.action();        }    }

这里我想给大家做一个扩展点,这也是我之前看到过一种校验写法。文章源自JAVA秀-https://www.javaxiu.com/38180.html

大家在真实的工作中肯定会遇到很多一些接口的校验,怎么去写这个校验逻辑,怎么做到代码的复用、抽象等这其实是一个比较难的问题!文章源自JAVA秀-https://www.javaxiu.com/38180.html

还是大致的来看下结构图吧!!!文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式&中介者模式文章源自JAVA秀-https://www.javaxiu.com/38180.html

demo代码,我也给大家写出来,需要注意的是我们需要实现 ApplicationContextAware 里面的afterPropertiesSet 方法。文章源自JAVA秀-https://www.javaxiu.com/38180.html

// 定义抽象校验方法public abstract class ValidatePlugin {    public abstract void validate();}// 抽象规则执行器public abstract class ValidatePluginExecute {    protected abstract List<ValidatePlugin> getValidatePlugins();    public void execute() {        final List<ValidatePlugin> validatePlugins = getValidatePlugins();        if (CollectionUtils.isEmpty(validatePlugins)) {            return;        }        for (ValidatePlugin validatePlugin : validatePlugins) {          // 执行校验逻辑,这里大家可以根据自己的实际业务场景改造            validatePlugin.validate();        }    }}// 具体测试规则@Component("validatePluginOne")public class ValidatePluginOne extends  ValidatePlugin {    @Override    public void validate() {        System.out.println("validatePluginOne 规则校验");    }}// 具体执行器,把需要执行的规则添加到 validatePlugins 中@Component("testValidatePlugin")public class TestValidatePlugin extends ValidatePluginExecute implements ApplicationContextAware, InitializingBean {    protected ApplicationContext applicationContext;    private List<ValidatePlugin> validatePlugins;    @Override    public void afterPropertiesSet() {      // 添加规则        validatePlugins = Lists.newArrayList();        validatePlugins.add((ValidatePlugin) this.applicationContext.getBean("validatePluginOne"));    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }    @Override    protected List<ValidatePlugin> getValidatePlugins() {        return this.validatePlugins;    }}// 测试demo  public static void main(String[] args) {        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");        TestValidatePlugin testValidatePlugin = (TestValidatePlugin) applicationContext.getBean("testValidatePlugin");        testValidatePlugin.execute();    }

这个只是一个简单的测试demo,为了让大家有一个思考,设计模式不一定是照搬代码。更多是开拓自己的视野,提升自己解决问题的能力。文章源自JAVA秀-https://www.javaxiu.com/38180.html

针对不同的一些接口,我们只需要在TestValidatePlugin 中添加具体校验规则就可以了,整体的扩展性就变高了,看上去也比较高大上。文章源自JAVA秀-https://www.javaxiu.com/38180.html

所以上面提到的命令模式、策略模式、工厂模式区别是什么呢?文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 命令模式:属于行为型设计模式,在命令模式中,不同的命令执行过程中会产生不同的目的结果,而且不同的命令是不能替换的。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 策略模式 :属于行为型设计模式,在策略模式中,重点在于针对每一种策略执行,解决根据运行时状态从一组策略中选择不同策略的问题文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • 工厂模式:属于创建型设计模式,在工厂模式中,重点在于封装对象的创建过程,这里的对象没有任何业务场景的限定,可以是策略,但也可以是其他东西文章源自JAVA秀-https://www.javaxiu.com/38180.html

所以针对设计模式,其实我理解的还是只说明了一个问题,不同的设计模式都是为了针对处理不同的场景,不同业务场景有不同的写法。文章源自JAVA秀-https://www.javaxiu.com/38180.html

中介者模式

中介者模式,看这个名字也能理解出来,定一个中间结构来方便管理下游组织。文章源自JAVA秀-https://www.javaxiu.com/38180.html

那么什么是中介模式呢?文章源自JAVA秀-https://www.javaxiu.com/38180.html

在GoF 中的《设计模式》中解释为:中介模式定义了一个单独的(中介)对象,来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互,来避免对象之间的直接交互。文章源自JAVA秀-https://www.javaxiu.com/38180.html

再来看看这个结构图吧:文章源自JAVA秀-https://www.javaxiu.com/38180.html

命令模式&中介者模式文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Mediator(抽象中介者):用来定义参与者与中介者之间的交互方式文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • ConcreteMediator(具体中介者):实现中介者定义的操作,即就是实现交互方式。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • Colleague(抽象同事角色):抽象类或者接口,主要用来定义参与者如何进行交互。文章源自JAVA秀-https://www.javaxiu.com/38180.html

  • ConcreteColleague(具有同事角色):很简单,就是具体的实现Colleague中的方法。文章源自JAVA秀-https://www.javaxiu.com/38180.html

    以上结构定义来自设计模式之美文章源自JAVA秀-https://www.javaxiu.com/38180.html

看这个结构图理解出来,其实是跟之前为大家写的一篇观察者模式有点相同的,感兴趣的同学可以再去复习一下。文章源自JAVA秀-https://www.javaxiu.com/38180.html

老规矩,还是具体举例代码实现一下文章源自JAVA秀-https://www.javaxiu.com/38180.html

高铁系统大家应该清楚有一个调度中心,用来控制每一辆高铁的进站顺序,如果没有这个调度中心,当同时有三量高铁都即将进站时,那他们就需要两两相护沟通。文章源自JAVA秀-https://www.javaxiu.com/38180.html

假设有其中的一辆动车没有沟通到,那就将发生不可估量的错误,所以就需要通过这个调度中心来处理这个通信逻辑,同时来管理当前有多少车辆等待进站等。文章源自JAVA秀-https://www.javaxiu.com/38180.html

// 抽象参与者, 也可以使用abstract 写法public interface Colleague {   // 沟通消息    void message();}// 抽象中介者public interface Mediator {    // 定义处理逻辑    void doEvent(Colleague colleague);}// 具体参与者@Componentpublic class MotorCarOneColleague implements Colleague {    @Override    public void message() {        // 模拟处理业务逻辑        System.out.println("高铁一号收到消息!!!");    }}@Componentpublic class MotorCarTwoColleague implements Colleague {    @Override    public void message() {        System.out.println("高铁二号收到消息!!!");    }}@Componentpublic class MotorCarThreeColleague implements Colleague {    @Override    public void message() {        System.out.println("高铁三号收到消息!!!");    }}// 具体中介者@Componentpublic class DispatchCenter implements Mediator {  // 管理有哪些参与者    @Autowired    private List<Colleague> colleagues;      @Override    public void doEvent(Colleague colleague) {        for(Colleague colleague1 :colleagues){            if(colleague1==colleague){                // 如果是本身高铁信息,可以处理其他的业务逻辑                // doSomeThing();                continue;            }          // 通知其他参与            colleague1.message();        }    }}// 测试demopublic static void main(String[] args) {     // 初始化spring容器        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");     // 获取中介者,调度中心        DispatchCenter dispatchCenter = (DispatchCenter) applicationContext.getBean("dispatchCenter");        // 一号高铁 发送消息出去        MotorCarOneColleague motorCarOneColleague =  (MotorCarOneColleague) applicationContext.getBean("motorCarOneColleague");     // 通过调度中心沟通信息        dispatchCenter.doEvent(motorCarOneColleague);        // result:高铁三号收到消息!!!        //         高铁二号收到消息!!!        // 二号高铁 发送消息出去        MotorCarTwoColleague  motorCarTwoColleague = (MotorCarTwoColleague)applicationContext.getBean("motorCarTwoColleague");        dispatchCenter.doEvent(motorCarTwoColleague);        // result:高铁一号收到消息!!!        //         高铁三号收到消息!!!    }

中介者模式demo代码就算完成了,通过这个demo大家应该能发现,中介者还是很好理解的。文章源自JAVA秀-https://www.javaxiu.com/38180.html

但是中介者的应用场景还是比较少见的,针对一些类依赖严重,形成的类似网状结构,改成一个类似与蒲公英一样结构,由中间向外扩散,来达到解耦合的效果。文章源自JAVA秀-https://www.javaxiu.com/38180.html

更多在一个UI界面控件里面比较常见,当然在Java里面java.util.Timer 也可以理解为中介者模式,因为它能控制内部线程如何去运行比如多久运行一次等。文章源自JAVA秀-https://www.javaxiu.com/38180.html

上面提到中介者和观察者模式很像,通过demo代码大家也能发现这一点文章源自JAVA秀-https://www.javaxiu.com/38180.html

观察者模式中观察者和被观察者我们基本时固定的,而中介者模式中,观察者和被观察者时不固定的,而且中介者可能会最后变成一个庞大的原始类。文章源自JAVA秀-https://www.javaxiu.com/38180.html

总结

命令模式:虽然不怎么常见,但是我们还是要区分它与工厂模式以及策略模式的区别是啥,应用场景是啥,能给我们带来什么思考。文章源自JAVA秀-https://www.javaxiu.com/38180.html

比如我最后的那个例子,命令模式可以实现命令的存储,本质是将命令维护在一个队列中,那么在我们的业务代码中 我们为什么不能也通过一个数组来维护一些接口校验依赖,里面存放需要校验的bean实例。来提高代码的复用性以及扩展性。文章源自JAVA秀-https://www.javaxiu.com/38180.html

中介模式:整体来说这个更加不怎么应用,虽然能起到对象的解耦合,但是也有副作用,而且在我们的真实业务场景中也很少会遇到这样的场景,了解一下实现原理即可,至于与观察者的区别,上面也有讲到,更多我们可能是已经在使用一些中间件消息队列去处理了。文章源自JAVA秀-https://www.javaxiu.com/38180.html

我是敖丙,你知道的越多,你不知道的越多,我们下期见!文章源自JAVA秀-https://www.javaxiu.com/38180.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
  • 版权声明:本站是JAVA秀团队的技术分享社区,我们会经常分享资源和教程。
  • 转载请注明:命令模式&中介者模式 - JAVA秀 ☜(ˆ▽ˆ)
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定