iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0
Software Development

也該是時候學學 Design Pattern 了系列 第 20

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 模式。


上一篇
Day 19: Behavioral patterns - Command
下一篇
Day 21: Behavioral patterns - Iterator
系列文
也該是時候學學 Design Pattern 了31

尚未有邦友留言

立即登入留言