设计模式之禅之混编【命令模式+责任链模式】

设计模式之禅之混编

命令模式+责任链模式

  • 搬移UNIX命令
    • 在Windows系统上使用UNIX命令
    • UNIX命令
      1. 命令名
      2. 选项
      3. 操作数
    • UNIX规定
      1. 命令名为小写字母
      2. 命令名、选项、操作树之间以空格分隔,空格数量不受限制
      3. 选项之间看可以组合使用,也可以单独拆分使用
      4. 选项以“-”开头
      • ls简单列出一个目录下的文件
      • ls-l详细列出目录下的文件
      • ls-a列出目录下包含的隐藏文件,主要是点号(.)开头的文件
      • ls-s列出文件的大小
  • 针对ls命令族,要求如下:
    1. 每一个ls命令都有操作数,默认操作数为当前目录
    2. 选项不可重复
    3. 每个选项返回不同的结果,也就是说每个选项应该由不同的业务逻辑来处理
    4. 为提高扩展性,ls应该对外封闭,减少外界访问ls命令族内部细节的可能性

命令选模式

  • 责任链模式
    • 只需一个参数传递到链首,就可以获得有个结果,中间是如何传递的以及由哪个逻辑解析都不需要外界模块关系
    • 命令族解析类图
    • 类图解释
      1. ComandName抽象类,所有的地命令都继承该类,它就是责任链中的handle类,负责链表的控制
      2. 每个命令族都有一个独立的抽象类,因为每个命令族都有其独特的个性
      3. Context负责建立一条命令链,并返回首节点供高层模块调用

派发命令选模式

  • 命令模式
    • 命令的解析由责任链解析,但是如此多的命令还需要一个派发的角色,输入一个命令,不管后台由谁来解析,返回一个结果就成,这就要用到命令模式,命令模式负责协调各个正确地传递到各个责任链的首节点,这就是它的任务
    • 类图
    • 类图解析
      1. ClassUtils工具类:主要职责是根据一个接口、父类查找到所有的子类,在不考虑的应用中,使用该类可以带来非常好的扩展性
      2. CommandVO:是命令的值对象,它把一个命令解析为命令名、选项、操作数
      3. CommandEnum:是枚举类型,是命令配置文件

整合这个两个模式

  • 类图

具体代码

  • 项目结构
  • 详细代码

    • AbstractLS.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public abstract class AbstractLS extends CommandName {
          // 默认参数
          public final static String DEFAULT_PARAM = "";
          //参数a
          public final static String A_PARAM="a";
          //参数|
          public final static String L_PARAM="|";
      
      }
      
    • ClassUtils.java

      package com.peng.zm;
      
      import java.io.File;
      import java.io.IOException;
      import java.net.URL;
      import java.util.ArrayList;
      import java.util.Enumeration;
      import java.util.List;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class ClassUtils {
          // 根据父类查找到所有的子类,默认情况下是子类和父类在同一个包名下
          public static List getSonClass(Class fatherClass) {
              // 定义一个返回值
              List returnClassList = new ArrayList();
              // 获得包名称
              String packageName = fatherClass.getPackage().getName();
              // 获得包中的所有类
              List packClasses = getClasses(packageName);
              // 判断是否是子类
              for (Class c : packClasses) {
                  if (fatherClass.isAssignableFrom(c) && !fatherClass.equals(c)) {
                      returnClassList.add(c);
                  }
              }
              return returnClassList;
          }
      
          // 从一个包中查出所有的类,在jar包中不能查找
          private static List getClasses(String packageName) {
              ClassLoader classLoader = Thread.currentThread()
                      .getContextClassLoader();
              String path = packageName.replace('.', '/');
              Enumeration resources = null;
              try {
                  resources = classLoader.getResources(path);
      
              } catch (IOException e) {
                  e.printStackTrace();
              }
              List dirs = new ArrayList();
              while (resources.hasMoreElements()) {
                  URL resource = resources.nextElement();
                  dirs.add(new File(resource.getFile()));
              }
              ArrayList classes = new ArrayList();
              for (File directory : dirs) {
                  classes.addAll(findClasses(directory, packageName));
              }
              return classes;
          }
      
          private static List findClasses(File directory, String packageName) {
              List classes = new ArrayList();
              if (!directory.exists()) {
                  return classes;
              }
              File[] files = directory.listFiles();
              for (File file : files) {
                  if (file.isDirectory()) {
                      assert !file.getName().contains(".") : classes
                              .addAll(findClasses(file,
                                      packageName + "." + file.getName()));
                  } else if (file.getName().endsWith(".class")) {
                      try{
                          classes.add(Class.forName(packageName+"."+file.getName().substring(0,file.getName().length()-6)));
                      }catch(ClassNotFoundException e){
                          e.printStackTrace();
                      }
                  }
      
              }
              return classes;
          }
      }
      
    • Client.java

      package com.peng.zm;
      
      import java.io.BufferedReader;
      import java.io.IOException;
      import java.io.InputStreamReader;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class Client {
          public static void main(String[] args) throws IOException {
              Invoker invoker = new Invoker();
              while (true) {
                  System.out.println("#");
                  String input = (new BufferedReader(new InputStreamReader(System.in)))
                          .readLine();
                  // 输入exit或quit则退出
                  if (input.equals("quit") || input.equals("exit")) {
                      return;
                  }
                  System.out.println("执行结果:" + invoker.exec(input));
              }
          }
      }
      
    • Command.java

      package com.peng.zm;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public abstract class Command {
          public abstract String execute(CommandVO vo);
      
          // 建立链表
          protected final List buildChain(
                  Class abstractClass) {
              // 取出所有命令下的子类
              List classes = ClassUtils.getSonClass(abstractClass);
              // 存放命令的实例,并建立链表关系
              List commandNameList = new ArrayList();
              for (Class c : classes) {
                  CommandName commandName = null;
                  try {
                      // 产生实例
                      commandName = (CommandName) Class.forName(c.getName())
                              .newInstance();
                  } catch (Exception e) {
                      // 异常处理
                  }
                  // 建立链表
                  if (commandNameList.size() > 0) {
                      commandNameList.add(commandName);
                  }
              }
              return commandNameList;
      
          }
      }
      
    • CommandEnum.java

      package com.peng.zm;
      
      import java.util.ArrayList;
      import java.util.List;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public enum CommandEnum {
          ls("com.peng.zm.LSCommand");
      
          private String value = "";
      
          // 定义构造函数,目的是Data(value)类型相匹配
      
          private CommandEnum(String value) {
              this.value = value;
          }
      
          public String getValue() {
              return value;
          }
      
          // 方式所有的enum对象
          public static List getNames() {
              CommandEnum[] commandEnum = CommandEnum.values();
              List names = new ArrayList();
              for (CommandEnum commandEnum2 : commandEnum) {
                  names.add(commandEnum2.name());
              }
              return names;
          }
      }
      
    • CommandName.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description 抽象命名类
       */
      public abstract class CommandName {
          private CommandName nextOperator;
      
          public final String handleMessage(CommandVO vo) {
              // 处理结果
              String result = "";
              // 判断是否是自己处理的参数
              if (vo.getParam().size() == 0
                      || vo.getParam().contains(this.getOperateParam())) {
                  result = this.echo(vo);
              } else {
                  if (this.nextOperator != null) {
                      result = this.nextOperator.handleMessage(vo);
                  } else {
                      result = "命令无法执行";
                  }
              }
              return result;
          }
      
          // 设置剩余参数有谁来处理
          public void setNext(CommandName _operator) {
              this.nextOperator = _operator;
          }
      
          // 每个处理者都要处理一个后缀参数
          protected abstract String getOperateParam();
      
          // 每个处理者都要实现必须处理任务
          protected abstract String echo(CommandVO vo);
      
      }
      
    • CommandVO.java

      package com.peng.zm;
      
      import java.util.ArrayList;
      import java.util.HashSet;
      import java.util.List;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class CommandVO {
          // 定义参数名与参数的分隔符,一般是空格
          public final static String DIVIDE_FLAG = " ";
          // 定义参数前的符号,Unix一般是-
          public final static String PREFIX = "-";
          // 命令名
          private String commandName = "";
          // 参数列表
          private ArrayList paramList = new ArrayList();
          // 操作数列表
          private ArrayList dataList = new ArrayList();
      
          // 通过构造函数来传递进来
          public CommandVO(String commandStr) {
              // 常规判断
              if (commandStr != null && commandStr.length() != 0) {
                  // 根据分隔符号拆分出执行符号
                  String[] complexStr = commandStr.split(CommandVO.DIVIDE_FLAG);
                  // 第一个参数是执行符号
                  this.commandName = complexStr[0];
                  // 把参数放到List
                  for (int i = 1; i < complexStr.length; i++) {
                      String str = complexStr[i];
                      // 包含前缀符号,认为是参数
                      if (str.indexOf(CommandVO.PREFIX) == 0) {
                          this.paramList
                                  .add(str.replace(CommandVO.PREFIX, "").trim());
                      } else {
                          // 操作数列表
                          this.dataList.add(str.trim());
                      }
                  }
              } else {
                  // 传递的命令错误
                  System.out.println("命令解析失败,必须传递一个命令才能执行!");
              }
          }
      
          // 得到命令名
          public String getCommandName() {
              return this.commandName;
          }
      
          /**
           * 功能: 返回值类型: 参数列表:
           * 
           * @return
           */
          public List getParam() {
              // 为了方便处理空参数
              if (this.paramList.size() == 0) {
                  this.paramList.add("");
              }
              return new ArrayList(new HashSet(this.paramList));
          }
      
          // 获得操作数
          public ArrayList getaData() {
              return this.dataList;
          }
      
          /**
           * 功能: 返回值类型: 参数列表:
           * 
           * @return
           */
          public String formatDate() {
              return this.getaData().toString();
          }
      
      }
      
    • FileManager.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class FileManager {
          // ls命令
          public static String ls(String path) {
              return "file1\nfile2\nfile3\nfile4";
          }
      
          // ls-l命令
          public static String ls_l(String path) {
              String str = "drw-rw-rw root system 1024 2018-1-10 18:00 file1/n";
              str += "drw-rw-rw root system 1024 2018-1-10 18:01 file2/n";
              str += "drw-rw-rw root system 1024 2018-1-10 18:02 file3/n";
              return str;
          }
      
          // ls-a命令
          public static String ls_a(String path) {
              return ".\n..\nfile1\nfile2\nfile3";
          }
      }
      
    • Invoker.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class Invoker {
          // 执行命令
          public String exec(String _commandStr) {
              // 返回值
              String result = "";
              // 首先解析命令
              CommandVO vo = new CommandVO(_commandStr);
              // 检查命令是否支持该命令
              if (CommandEnum.getNames().contains(vo.getCommandName())) {
                  // 产生命令对象
                  String className = CommandEnum.valueOf(vo.getCommandName())
                          .getValue();
                  Command command;
                  try {
                      command = (Command) Class.forName(className).newInstance();
                      result = command.execute(vo);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              } else {
                  result = "无法执行命令,请检查命令格式!";
              }
              return result;
          }
      }
      
    • LS_A.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class LS_A extends AbstractLS {
          @Override
          protected String getOperateParam() {
              return super.A_PARAM;
          }
      
          @Override
          protected String echo(CommandVO vo) {
              return FileManager.ls_a(vo.formatDate());
          }
      
      }
      
    • LS_L.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class LS_L extends AbstractLS {
          @Override
          protected String getOperateParam() {
              return super.L_PARAM;
          }
      
          @Override
          protected String echo(CommandVO vo) {
              return FileManager.ls_l(vo.formatDate());
          }
      
      }
      
    • LS.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class LS extends AbstractLS {
      
          // 参数为空
          @Override
          protected String getOperateParam() {
              return super.DEFAULT_PARAM;
          }
      
          // 最简单的ls命令
          @Override
          protected String echo(CommandVO vo) {
              return FileManager.ls(vo.formatDate());
          }
      
      }
      
    • LSCommand.java

      package com.peng.zm;
      
      /**
       * @author kungfu~peng
       * @data 2018年1月10日
       * @description
       */
      public class LSCommand extends Command {
          @Override
          public String execute(CommandVO vo) {
              String result = "";
              if (vo.getCommandName().equalsIgnoreCase("ls")
                      && vo.getParam().size() == 1 && vo.getaData().size() == 0) {
                  if (vo.getParam().contains("") || vo.getParam().contains("A")) {
                      AbstractLS al = new LS();
                      result = al.echo(vo);
                  } else if (vo.getParam().contains("a")
                          || vo.getParam().contains("A")) {
                      AbstractLS al = new LS_A();
                      result = al.echo(vo);
                  } else if (vo.getParam().contains("l")
                          || vo.getParam().contains("L")) {
                      AbstractLS al = new LS_L();
                      result = al.echo(vo);
                  }
              } else {
                  result = "../../file/all../";
              }
              return result;
          }
      }
      
    • 执行结果

      #
      ls
      执行结果:file1
      file2
      file3
      file4
      #
      ls -a
      执行结果:.
      ..
      file1
      file2
      file3
      #
      ls -l
      执行结果:drw-rw-rw root system 1024 2018-1-10 18:00 file1/ndrw-rw-rw root system 1024 2018-1-10 18:01 file2/ndrw-rw-rw root system 1024 2018-1-10 18:02 file3/n
      #
      

最佳实践

  • 责任链模式
    • 负责对命令参数进行解析,而且所有的扩展都是增加链数量和节点,不涉及原有的代码变更
  • 命令模式
    • 负责命令的分发,把适当的命令发送到指定的链上
  • 模板方法模式
    • 建立链的方法作为模板办法,调用基本方法,基本方法由各个实现类实现,非常有利于扩展
  • 迭代器模式
    • 在for循环中我们多次用到类似for(Class c:classes)的结构,是谁来支撑该方法运行?当然是迭代器模式

未处理的项

  • ls-l-a
    • 处理方式
      1. 独立处理:“is-l-a”等同于“ls-a”命令,可以把“ls-la”中的选项“la”作为一个参数来进行处理,扩展一个类就可以了,该方法的缺点就是类膨胀的太大,但是简单
      2. 混合处理:修正命令链处理链,每个命令处理节点运行完毕后,继续由后续节点处理,最终由Command类组装结果,根据每个节点处理结果,组合后生产完整的返回信息

关于该框架

  • 该框架还有一个名字,叫做命令链模式,具体来说就是命令模式作为责任链模式的排头兵,由命令模式分发具体的消息到责任链模式。

你可能感兴趣的:(设计模式专栏,设计模式之禅)