Day 20: Behavioral patterns - Interpreter

目的

取得一段讯息後,解析、转译成具有特定含义的讯息。

说明

简单来说,就是讯息的转换器。

当有分析讯息的需求,其组成是具有逻辑可判断,判断後的讯息可被理解、执行时,就能采用此方法

在「物件导向设计模式」一书中,此模式在判断上依赖两个物件:TerminalExpression 以及 NonTerminalExpression,前者负责解析以及执行工作、後者则是一个集合,解析片段讯息要传给哪个 TerminalExpressionNonTerminalExpression

本次实作省略 NonTerminalExpression

这次的作法是:

  1. 建立负责转换的虚拟层亲代物件,开规格要求解析的方法。
  2. 建立负责实作转换细节的子代物件(TerminalExpressionInterpreter 物件)。
  3. 输入文字,进行转换、解析。

以下范例以「简易版的摩斯密码」为核心制作。

UML 图

Interpreter Pattern UML Diagram

使用 Java 实作

储存原始讯息物件:Morse

public class Morse {
    private String context;

    public Morse(String context) {
        this.context = context;
    }

    public String getContext() {
        return context;
    }
}

负责转换的虚拟层亲代物件:Expression

public interface Expression {
    public void interpret(Morse context);
}

实作转换细节的子代物件:OneCharExpressionTwoCharsExpressionThreeCharsExpressionFourCharsExpressionFiveCharsExpressionTerminalExpressionInterpreter 物件)

public class OneCharExpression implements Expression {
    public OneCharExpression() {
    }

    @Override
    public void interpret(Morse context) {
        String code = context.getContext();
        String result = null;

        if (code.contentEquals(".")) {
            result = "E";
        } else if (code.contentEquals("-")) {
            result = "T";
        } else {
            result = "";
        }

        System.out.print(result);
    }
}

public class TwoCharsExpression implements Expression {
    public TwoCharsExpression() {
    }

    @Override
    public void interpret(Morse context) {
        String code = context.getContext();
        String result = null;

        if (code.contentEquals("..")) {
            result = "I";
        } else if (code.contentEquals(".-")) {
            result = "A";
        } else if (code.contentEquals("-.")) {
            result = "N";
        } else if (code.contentEquals("--")) {
            result = "M";
        } else {
            result = "";
        }

        System.out.print(result);
    }
}

public class ThreeCharsExpression implements Expression {
    public ThreeCharsExpression() {
    }

    @Override
    public void interpret(Morse context) {
        String code = context.getContext();
        String result = null;

        if (code.contentEquals("...")) {
            result = "S";
        } else if (code.contentEquals("-..")) {
            result = "D";
        } else if (code.contentEquals(".-.")) {
            result = "R";
        } else if (code.contentEquals("..-")) {
            result = "U";
        } else if (code.contentEquals(".--")) {
            result = "W";
        } else if (code.contentEquals("-.-")) {
            result = "K";
        } else if (code.contentEquals("--.")) {
            result = "G";
        } else if (code.contentEquals("---")) {
            result = "O";
        } else {
            result = "";
        }

        System.out.print(result);
    }
}

public class FourCharsExpression implements Expression {
    public FourCharsExpression() {
    }

    @Override
    public void interpret(Morse context) {
        String code = context.getContext();
        String result = null;

        if (code.contentEquals("....")) {
            result = "H";
        } else if (code.contentEquals("-...")) {
            result = "B";
        } else if (code.contentEquals(".-..")) {
            result = "L";
        } else if (code.contentEquals("..-.")) {
            result = "F";
        } else if (code.contentEquals("...-")) {
            result = "V";
        } else if (code.contentEquals("--..")) {
            result = "Z";
        } else if (code.contentEquals("-.-.")) {
            result = "C";
        } else if (code.contentEquals("-..-")) {
            result = "X";
        } else if (code.contentEquals(".--.")) {
            result = "P";
        } else if (code.contentEquals(".---")) {
            result = "J";
        } else if (code.contentEquals("--.-")) {
            result = "Q";
        } else if (code.contentEquals("-.--")) {
            result = "Y";
        } else {
            result = "";
        }

        System.out.print(result);
    }
}

public class FiveCharsExpression implements Expression {
    public FiveCharsExpression() {
    }

    @Override
    public void interpret(Morse context) {
        String code = context.getContext();
        String result = null;

        if (code.contentEquals(".----")) {
            result = "1";
        } else if (code.contentEquals("..---")) {
            result = "2";
        } else if (code.contentEquals("...--")) {
            result = "3";
        } else if (code.contentEquals("....-")) {
            result = "4";
        } else if (code.contentEquals(".....")) {
            result = "5";
        } else if (code.contentEquals("-....")) {
            result = "6";
        } else if (code.contentEquals("--...")) {
            result = "7";
        } else if (code.contentEquals("---..")) {
            result = "8";
        } else if (code.contentEquals("----.")) {
            result = "9";
        } else if (code.contentEquals("-----")) {
            result = "0";
        } else {
            result = "";
        }

        System.out.print(result);
    }
}

测试,输入一段摩斯密码,并且解译出含义:MorseInterpreterSample

public class MorseInterpreterSample {
    public static void main(String[] args) {
        Morse context = new Morse("- .- .. .-- .- -. -. --- .----");
        String[] contextArray = context.getContext().split(" ");

        Expression morseExpression = null;

        for (String code : contextArray) {
            int codeLength = code.length();

            switch (codeLength) {
                case 1:
                    morseExpression = new OneCharExpression();
                    break;
                case 2:
                    morseExpression = new TwoCharsExpression();
                    break;
                case 3:
                    morseExpression = new ThreeCharsExpression();
                    break;
                case 4:
                    morseExpression = new FourCharsExpression();
                    break;
                case 5:
                    morseExpression = new FiveCharsExpression();
                    break;
                default:
                    break;
            }

            morseExpression.interpret(new Morse(code));
        }
    }
}

使用 JavaScript 实作

储存原始讯息物件:Morse

class Morse {
  /** @param {string} context */
  constructor(context) {
    this.context = context;
  }

  getContext() {
    return this.context;
  }
}

负责转换的亲代物件:Expression

/** @interface */
class Expression {
  /** @param {Morse} context */
  interpret(context) { return; }
}

实作转换细节的子代物件:OneCharExpressionTwoCharsExpressionThreeCharsExpressionFourCharsExpressionFiveCharsExpression(TerminalExpression)

class OneCharExpression extends Expression {
  constructor() {
    super();
  }

  /**
   * @override
   * @param {Morse} context
   */
  interpret(context) {
    const code = context.getContext();
    let result = null;

    if (code === ".") {
      result = "E";
    } else if (code === "-") {
      result = "T";
    } else {
      result = "";
    }

    return result;
  }
}

class TwoCharsExpression extends Expression {
  constructor() {
    super();
  }

  /**
   * @override
   * @param {Morse} context
   */
  interpret(context) {
    const code = context.getContext();
    let result = null;

    if (code === "..") {
      result = "I";
    } else if (code === ".-") {
      result = "A";
    } else if (code === "-.") {
      result = "N";
    } else if (code === "--") {
      result = "M";
    } else {
      result = "";
    }

    return result;
  }
}

class ThreeCharsExpression extends Expression {
  constructor() {
    super();
  }

  /**
   * @override
   * @param {Morse} context
   */
  interpret(context) {
    const code = context.getContext();
    let result = null;

    if (code === "...") {
      result = "S";
    } else if (code === "-..") {
      result = "D";
    } else if (code === ".-.") {
      result = "R";
    } else if (code === "..-") {
      result = "U";
    } else if (code === ".--") {
      result = "W";
    } else if (code === "-.-") {
      result = "K";
    } else if (code === "--.") {
      result = "G";
    } else if (code === "---") {
      result = "O";
    } else {
      result = "";
    }

    return result;
  }
}

class FourCharsExpression extends Expression {
  constructor() {
    super();
  }

  /**
   * @override
   * @param {Morse} context
   */
  interpret(context) {
    const code = context.getContext();
    let result = null;

    if (code === "....") {
      result = "H";
    } else if (code === "-...") {
      result = "B";
    } else if (code === ".-..") {
      result = "L";
    } else if (code === "..-.") {
      result = "F";
    } else if (code === "...-") {
      result = "V";
    } else if (code === "--..") {
      result = "Z";
    } else if (code === "-.-.") {
      result = "C";
    } else if (code === "-..-") {
      result = "X";
    } else if (code === ".--.") {
      result = "P";
    } else if (code === ".---") {
      result = "J";
    } else if (code === "--.-") {
      result = "Q";
    } else if (code === "-.--") {
      result = "Y";
    } else {
      result = "";
    }

    return result;
  }
}

class FiveCharsExpression extends Expression {
  constructor() {
    super();
  }

  /**
   * @override
   * @param {Morse} context
   */
  interpret(context) {
    const code = context.getContext();
    let result = null;

    if (code === ".----") {
      result = "1";
    } else if (code === "..---") {
      result = "2";
    } else if (code === "...--") {
      result = "3";
    } else if (code === "....-") {
      result = "4";
    } else if (code === ".....") {
      result = "5";
    } else if (code === "-....") {
      result = "6";
    } else if (code === "--...") {
      result = "7";
    } else if (code === "---..") {
      result = "8";
    } else if (code === "----.") {
      result = "9";
    } else if (code === "-----") {
      result = "0";
    } else {
      result = "";
    }

    return result;
  }
}

测试,输入一段摩斯密码,并且解译出含义:MorseInterpreterSample

const morseInterpreterSample = () => {
  const context = new Morse("- .- .. .-- .- -. -. --- .----");
  const contextArray = context.getContext().split(" ");

  let morseExpression = null;
  let result = "";

  for (const code of contextArray) {
    const codeLength = code.length;

    switch (codeLength) {
      case 1:
        morseExpression = new OneCharExpression();
        break;
      case 2:
        morseExpression = new TwoCharsExpression();
        break;
      case 3:
        morseExpression = new ThreeCharsExpression();
        break;
      case 4:
        morseExpression = new FourCharsExpression();
        break;
      case 5:
        morseExpression = new FiveCharsExpression();
        break;
      default:
        break;
    }

    result += morseExpression.interpret(new Morse(code));
  }

  console.log(result);
};

morseInterpreterSample();

总结

Interpreter 模式令人感到困惑的点在於范例不多,即使找到一两个范例後,很难找到现实可用的场合,因为有替代的方案:

  • 如果文字要解析的讯息不多,使用多个 if - else if - else 就能处理。
  • 如果文字要解析大量的讯息,AI 领域内有个分支是「自然语言处理(Natural Language Processing,NLP)」,可以更有效率地处理。

用NLP更好

整体看下去,让我觉得这已经是因为科技进步、时代变迁後,逐渐没有发挥之处的老东西。

明天将介绍 Behavioural patterns 的第四个模式:Iterator 模式。


<<:  Day22_控制项(A17营运持续管理之资讯安全层面)-2021/10/05

>>:  30天打造品牌特色电商网站 Day.21 图片展示设计

Day 29 关於结对编程

关於结对编程 通常大家对结对编程 ( Pair Programming )的了解就是两个人一起写 C...

Consistency and Consensus (3-1) - Ordering Guarantees

顺序这件事在 Design Data Intensive Applications 这本书中重复到提...

【Day30】我结束,换你了

挖,我居然真的完成这 30 天发文的活动,我觉得我好棒棒 XD。 统整 30 天的内容 给 Azur...

D7 allauth 采坑日记 Extending & Substituting User model (2)

接续上一篇 这次要讲的是我研究中途试过的另一个方法 Substituting 这其实是我一开始的想法...