zoukankan      html  css  js  c++  java
  • 【设计模式(14)】行为型模式之命令模式

    个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道

    如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充


    前言

    生活中,一件事情的请求者和执行者不一定是同一个人。比如我们申请售后的时候,将需要的信息等告知售后,然后由售后来安排处理,我们可以去做别的事情了;电视遥控器向电视发送命令,电视来执行;老板给我们分配任务等等。。。

    好处是我们不需要将请求者与执行者建立绑定关系,也不需要占用请求者的时间。

    在开发中,我们也会需要解除请求者和执行者之间的耦合,一方面利于扩展,一方面使得两者不必同步运作。

    命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。

    请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。


    1.介绍

    使用目的:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开

    使用时机:需要将方法的请求者与方法的实现者解耦,进而让两者之间通过命令对象进行沟通,方便将命令对象进行储存、传递、调用、增加与管理。

    解决问题:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的

    实现方法:请求者通过调用者,再由调用者来调用接受者执行命令。

    应用实例:

    • 下载管理器,请求者将请求发送给管理器,由管理器来执行并管理下载任务
    • 命令池,请求者将命令组装好后传入命令池,由命令池执行并管理下载任务

    优点

    1. 降低了系统耦合度
    2. 新的命令可以很容易添加到系统中
    3. 释放了请求者,使其不必等待执行结果

    缺点:使用命令模式可能会导致某些系统有过多的具体命令类


    2.结构

    命令模式包含以下主要角色

    • 抽象命令类(Command)角色:声明执行命令的接口,拥有执行命令的抽象方法 execute()。
    • 具体命令(Concrete Command)角色:是抽象命令类的具体实现类,它拥有接收者对象,并通过调用接收者的功能来完成命令要执行的操作。
    • 实现者/接收者(Receiver)角色:执行命令功能的相关操作,是具体命令对象业务的真正实现者。
    • 调用者/请求者(Invoker)角色:是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。

    image-20201103181234421

    • 客户端调用invoker
    • invoker持有Commond对象,并执行execute()命令
    • ConcreteCommand需要实现Commond接口,并持有Receiver对象,在execute()方法中调用
    • Receiver则需要定义相关的方法,以供ConcreteCommand调用

    3.实现步骤

    1. 定义接收者

      class ReceiverA {
          public void action() {
              System.out.println("执行方法A");
          }
      }
      
      class ReceiverB {
          public void action() {
              System.out.println("执行方法B");
          }
      }
      

      接收者里面负责定义具体需要执行的相关方法

    2. 定义抽象命令类

      interface Command {
          void execute();
      }
      

      用于规范命令类提供的方法接口

    3. 定义具体命令类

      class ConcreteCommandA implements Command {
          private ReceiverA receiver;
      
          public ConcreteCommandA() {
              receiver = new ReceiverA();
          }
      
          @Override
          public void execute() {
              receiver.action();
          }
      }
      
      class ConcreteCommandB implements Command {
          private ReceiverB receiver;
      
          public ConcreteCommandB() {
              receiver = new ReceiverB();
          }
      
          @Override
          public void execute() {
              receiver.action();
          }
      }
      

      此类为核心,需要在此处实现抽象命令类,并持有接收者,并定义命令执行的逻辑

    4. 定义调用者

      class Invoker {
          private Command command;
      
          public void setCommand(Command command) {
              this.command = command;
          }
      
          public void call() {
              if (null != command) {
                  command.execute();
              }
          }
      }
      

      持有命令对象,并提供设置和执行命令的方法

    完整代码

    package com.company.test.command;
    
    class ReceiverA {
        public void action() {
            System.out.println("执行方法A");
        }
    }
    
    class ReceiverB {
        public void action() {
            System.out.println("执行方法B");
        }
    }
    
    interface Command {
        void execute();
    }
    
    class ConcreteCommandA implements Command {
        private ReceiverA receiver;
    
        public ConcreteCommandA() {
            receiver = new ReceiverA();
        }
    
        @Override
        public void execute() {
            receiver.action();
        }
    }
    
    class ConcreteCommandB implements Command {
        private ReceiverB receiver;
    
        public ConcreteCommandB() {
            receiver = new ReceiverB();
        }
    
        @Override
        public void execute() {
            receiver.action();
        }
    }
    
    class Invoker {
        private Command command;
    
        public void setCommand(Command command) {
            this.command = command;
        }
    
        public void call() {
            if (null != command) {
                command.execute();
            }
        }
    }
    
    public class CommandTest {
        public static void main(String[] args) {
            Invoker invoker = new Invoker();
    
            Command commandA = new ConcreteCommandA();
            invoker.setCommand(commandA);
            invoker.call();
    
            Command commandB = new ConcreteCommandB();
            invoker.setCommand(commandB);
            invoker.call();
        }
    }
    

    运行结果

    image-20201103183112261

    4.扩展实战

    目标需求如下:

    • 设计一个命令池,被多个命令类共享

    • 客户端调用请求者的方法,由请求者组装命令,并交付给命令池

    • 命令池会按顺序执行接收到的命令

      • 命令按照目标不同进行分组
      • 同一组命令按照先后执行,如果某个命令执行时间超过2s则不再等待,执行下一条命令
      • 每条命令均需要回调,即便超时

    实际可能的业务场景如下

    • 项目需要向多个设备下发命令,通过同一个的命令池管理

    • 对于同一个设备的命令需要按顺序执行,若某条命令完成或者超时则执行下一条,但每条命令均必须有回调

      因此不考虑超时的情况下,同一设备的命令为堵塞队列

    • 对于不同设备,之间的命令不能堵塞,不能相互影响

    • 系统内的命令只负责调用设备,具体设备业务由设备自己执行


    测试客户端代码:

    public class CommandPoolTest {
        public static void main(String[] args) {
            CommandPool commandPool = new CommandPool();
            CommandInterface command_1 = new CommandImpl(commandPool);
            command_1.doAB();
            command_1.doABC();
    
            while(true){}
        }
    }
    
    • 一共下发两条命令,分别是ABABC,那么全部子命令应该是ABABC
    • 系统中任务等待时间为1秒,超时则执行下一条命令
    • 代码中命令AC都是瞬时任务,B为持续3秒的任务,因此B会超时
    • 系统中我们添加了两个目标,因此每个命令都会发送到这两个目标

    部分运行结果:

    image-20201106174826189

    • 第一条命令A正常执行,并得到反馈
    • 第二条命令B下发,但未得到反馈(超时)
    • 第三条命令A下发,并得到反馈

    完整的运行结果中,下发命令的顺序是ABABC,而收到反馈的顺序是AACBB,因为两次B都是超时完成


    完整Demo地址https://gitee.com/echo_ye/practice/tree/master/src/main/java/com/company/commandPool


    后记

    设计模式只是提供了一种解决某个问题的思路,在实际开发中往往需要多种设计模式协同使用,并且需要根据需求扩展和变型

    比如这个demo,初略看了一下,使用了命令模式、享元模式、代理模式、单例模式等,还有我也分不清楚了。。。

    总之,最终目的就是实现我们的需求,而设计模式则提供了解决的思路


    作者:Echo_Ye

    WX:Echo_YeZ

    Email :echo_yezi@qq.com

    个人站点:在搭了在搭了。。。(右键 - 新建文件夹)

  • 相关阅读:
    SpringBoot入门
    Maven相关
    IO流之RandomAccessFile和File
    javaSE之运行时异常和编译时异常
    Map.Entry的由来和使用
    Oracle常用基础语法(未完待补和操作)
    Oracle存储过程和存储函数
    论文阅读笔记(三十七)【AAAI2020】:Frame-Guided Region-Aligned Representation for Video Person Re-identification
    论文阅读笔记(三十六)【AAAI2020】:Relation-Guided Spatial Attention and Temporal Refinement for Video-based Person Re-Identification
    论文阅读笔记(三十五)【CVPR2019】:Perceive Where to Focus: Learning Visibility-aware Part-level Features for Partial Person Re-identification
  • 原文地址:https://www.cnblogs.com/silent-bug/p/13938382.html
Copyright © 2011-2022 走看看