デザインパターン-インタープリターパターン

1はじめに

ソフトウェア開発では、いくつかの問題が繰り返し発生し、特定の類似点と規則性があります。それらを単純な言語に要約すると、これらの問題インスタンスはその言語のいくつかの文になるため、「コンパイラーの原則」のインタープリター・モードを使用して実装できます。

インタープリターモードの使用例は少ないですが、上記の特性を満たし、あまり高い運用効率を必要としないアプリケーション例では、インタープリターモードを使用すると効果が非常に高くなります。動作原理と指示。

2.モデルの定義と特徴

インタープリターモードの定義:分析オブジェクトの言語を定義し、言語の文法表現を定義してから、その言語の文を解釈するパーサーを設計します。つまり、コンパイルされた言語を使用して、アプリケーションの例を分析します。このモードは、特定のコンテキストを解釈する文法式処理のインターフェイスを実装します。

ここで言及されている文法と文の概念は、コンパイルの原則の説明と同じです。「文法」は言語の文法規則を指し、「文」は言語セットの要素です。たとえば、中国語の文章はたくさんありますが、「私は中国語です」もそのひとつです。文法ツリーを使って、中国語で直感的に文章を書くことができます。

インタプリタモードは動作に似たモードであり、その主な利点は次のとおりです。

  1. 優れたスケーラビリティ。クラスは、インタープリターモードで言語の文法規則を表すために使用されるため、継承などのメカニズムを介して文法を変更または拡張できます。
  2. 簡単に達成できます。構文ツリーの各式ノードクラスは類似しているため、その文法を実装する方が簡単です。

インタプリタモードの主な欠点は次のとおりです。

  1. 実行効率が低い。インタプリタモードは通常、多数のループと再帰呼び出しを使用します。説明する文が複雑になると、実行速度が非常に遅くなり、コードのデバッグプロセスも面倒になります。
  2. クラスの拡張が発生します。インタプリタモードの各ルールは、少なくとも1つのクラスを定義する必要があります。文法ルールが多いと、クラスの数が急激に増加し、システムの管理と保守が困難になります。
  3. 適用可能なシナリオは比較的少ないです。ソフトウェア開発では、言語文法を定義する必要のあるアプリケーション例が非常に少ないため、このモードが使用されることはめったにありません。

3.パターンの構造と実現

通訳モードは、単純な言語のコンパイルや分析でよく使用されます。その構造と実装を習得するには、まず、コンパイルの原則における「文法、文、文法ツリー」の概念およびその他の関連概念を理解する必要があります。

1)文法

文法は、言語の文法構造を説明するために使用される正式な規則です。ルールのないルールはありません。たとえば、完全な愛のルールは「お互いに魅力的で、ひたむきな愛情で、どちらの当事者にも愛の経験がない」と信じている人もいます。機械語であろうと自然言語であろうと、独自の文法規則があります。例えば、中国語の「文」の文法は次のとおりです。

〈文〉 :: = 〈件名〉 〈述語〉 〈オブジェクト〉
〈件名〉 :: = 〈代名詞〉 | 〈名詞〉
〈述語〉 :: = 〈動詞〉
〈オブジェクト〉 :: = 〈代名詞〉 | 〈名詞〉
<代名詞>あなた|私|彼
<名詞> 7人の大学生IXiaoxiaI英語
<動詞> :: =はい|学ぶ

注:ここでの記号「:: =」は「定義」を意味します。「<」および「>」で囲まれているのは非終端記号であり、囲まれていないのは終端記号です。

2)文

文は言語の基本単位であり、言語セットの要素であり、終端記号で構成されており、「文法」から推測できます。たとえば、上記の文法は「私は大学生です」と推測できるので、文です。

3)構文木

文法ツリーは、文構造のツリー表現の一種であり、文の派生結果を表し、文の文法構造のレベルを理解するのに役立ちます。図1に、「私は大学生です」の構文木を示します。

ここに画像の説明を挿入します

上記の基本的な知識があれば、インタプリタモードの構造を簡単に紹介できます。インタープリターパターンの構造はコンポジションパターンに似ていますが、コンポジションパターンよりも多くの要素が含まれており、コンポジションパターンはオブジェクト構造パターンであり、インタープリターパターンは動作に似たパターンです。

1.モデルの構造

通訳モードには、次の主な役割が含まれます

  1. 抽象表現(抽象表現)の役割:インタプリタのインターフェイスを定義し、インタプリタの解釈操作に同意し、主に解釈メソッドinterpret()を含みます。
  2. ターミナル式の役割:これは、文法のターミナルに関連する操作を実装するために使用される抽象式のサブクラスです。文法の各ターミナルには、それに対応する特定のターミナル式があります。
  3. 非終端式の役割:これは抽象式のサブクラスでもあり、文法の非終端記号に関連する操作を実装するために使用されます。文法の各規則は非終端式に対応します。
  4. コンテキストロール:通常、各インタープリターに必要なデータまたは共通の機能が含まれ、通常、すべてのインタープリターが共有するデータを転送するために使用され、後続のインタープリターはここからこれらの値を取得できます。
  5. クライアント:主なタスクは、分析が必要な文または式をインタプリタオブジェクトによって記述された抽象構文ツリーに変換してから、インタプリタの解釈メソッドを呼び出すことです。もちろん、インタプリタの解釈メソッドは次のようにすることもできます。環境ロールを介して間接的にアクセスされます。

ここに画像の説明を挿入します

2.パターンの実装

    package net.biancheng.c.interpreter;
    //抽象表达式类
    interface AbstractExpression {
    
    
        public void interpret(String info);    //解释方法
    }
    //终结符表达式类
    class TerminalExpression implements AbstractExpression {
    
    
        public void interpret(String info) {
    
    
            //对终结符表达式的处理
        }
    }
    //非终结符表达式类
    class NonterminalExpression implements AbstractExpression {
    
    
        private AbstractExpression exp1;
        private AbstractExpression exp2;
        public void interpret(String info) {
    
    
            //非对终结符表达式的处理
        }
    }
    //环境类
    class Context {
    
    
        private AbstractExpression exp;
        public Context() {
    
    
            //数据初始化
        }
        public void operation(String info) {
    
    
            //调用相关表达式类的解释方法
        }
    }

4.パターンの適用例

【例1】通訳モードで「ShaoYuetong」バスカードリーダープログラムを設計します。

注:「Shaoyuetong」バスカードリーダーが乗客の身元を特定できる場合、「Shaoguan」または「Guangzhou」「高齢者」、「女性」、「子供」の場合、無料で乗車できます。2元。

分析:この例では、「インタープリターモード」設計がより適切であり、最初に次のように文法規則を設計します。

<式> :: = <都市>的<人>
<都市> :: =韶関|広州
<人> :: =高齢者|女性|子供

次に、文法規則に従って、以下の手順に従ってバスカードリーダープログラムのクラス図を設計します。

  • 解釈メソッドinterpret(String info)を含む抽象式(Expression)インターフェースを定義します。
  • set(Set)クラスを使用して条件を満たす都市または人々を格納し、抽象式インターフェイスに解釈メソッドinterpret(Stringinfo)を実装して分析する文字を決定する、端末式(Terminal Expression)クラスを定義します。文字列がセット内のターミネータであるかどうか。
  • 抽象式のサブクラスでもある非終端式(AndExpressicm)クラスを定義します。これには、条件を満たす都市の終端式オブジェクトと、条件を満たす人の終端記号オブジェクトが含まれ、interpret(を実装します。文字列info)メソッドは、分析された文字列が条件を満たす都市の有資格者であるかどうかを判断するために使用されます。
  • 最後に、インタープリターに必要なデータを含む環境(Context)クラスを定義し、端末式の初期化を完了し、式オブジェクトの解釈メソッドを呼び出して文字列Explainを分析するメソッドfreeRide(String info)を定義します。 。構造図を図3に示します。

ここに画像の説明を挿入します

package net.biancheng.c.interpreter;

import java.util.*;

/*文法规则
  <expression> ::= <city>的<person>
  <city> ::= 韶关|广州
  <person> ::= 老人|妇女|儿童
*/
public class InterpreterPatternDemo {
    
    
    public static void main(String[] args) {
    
    
        Context bus = new Context();
        bus.freeRide("韶关的老人");
        bus.freeRide("韶关的年轻人");
        bus.freeRide("广州的妇女");
        bus.freeRide("广州的儿童");
        bus.freeRide("山东的儿童");
    }
}

//抽象表达式类
interface Expression {
    
    
    public boolean interpret(String info);
}

//终结符表达式类
class TerminalExpression implements Expression {
    
    
    private Set<String> set = new HashSet<String>();

    public TerminalExpression(String[] data) {
    
    
        for (int i = 0; i < data.length; i++) set.add(data[i]);
    }

    public boolean interpret(String info) {
    
    
        if (set.contains(info)) {
    
    
            return true;
        }
        return false;
    }
}

//非终结符表达式类
class AndExpression implements Expression {
    
    
    private Expression city = null;
    private Expression person = null;

    public AndExpression(Expression city, Expression person) {
    
    
        this.city = city;
        this.person = person;
    }

    public boolean interpret(String info) {
    
    
        String s[] = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }
}

//环境类
class Context {
    
    
    private String[] citys = {
    
    "韶关", "广州"};
    private String[] persons = {
    
    "老人", "妇女", "儿童"};
    private Expression cityPerson;

    public Context() {
    
    
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
    
    
        boolean ok = cityPerson.interpret(info);
        if (ok) System.out.println("您是" + info + ",您本次乘车免费!");
        else System.out.println(info + ",您不是免费人员,本次乘车扣费2元!");
    }
}

あなたは韶関の老人です、今回は無料です!
韶関の若者、あなたは自由人ではありません、あなたはこの旅行のために2元を差し引きます!
あなたは広州の女性です、そしてあなたの乗車は今回無料です!
あなたは広州の子供です、そしてあなたの乗車は今回無料です!
山東省の子供たち、あなたは自由な人ではありません、あなたはこの旅行のために2元を差し引きます!

5.モードのアプリケーションシナリオ

インタプリタモードの構造と特性は上で紹介され、そのアプリケーションシナリオは以下で分析されます。

  1. 言語の文法が比較的単純で、実行効率が重要な問題ではない場合。
  2. 問題が再発し、簡単な言語で表現できる場合。
  3. 言語を解釈して実行する必要があり、その言語の文をXMLドキュメントの解釈などの抽象構文木として表現できる場合。

注:インタープリターモードは、効率、パフォーマンス、およびメンテナンスの問題を引き起こすため、実際のソフトウェア開発で使用されることはめったにありません。式の解釈に遭遇した場合は、Expression4JまたはJepを使用してJavaで設計できます。

6.パターンの拡大

プロジェクト開発では、データ式を分析および計算する場合、インタープリターモードを使用して設計する必要はありません。Javaには、Expression4J、MESP(Math Expression String Parser)、Jepなどの強力な数式パーサーが用意されています。 、彼らはいくつかの複雑な文法を説明することができ、強力で使いやすいです。

次に、このツールキットの使用方法を紹介する例としてJepを取り上げます。Jepは、Java式パーサーの略語です。つまり、数式を変換および計算するために使用されるJavaライブラリであるJava式パーサーです。このライブラリを介して、ユーザーは文字列の形式で任意の数式を入力し、結果をすばやく計算できます。また、Jepは、一般的に使用される多くの数学関数や定数など、ユーザー定義の変数、定数、関数をサポートしています。

紹介する例として、預金利息の計算を見てみましょう。預金利息の計算式は、元金x利率x時間=利息であり、関連するコードは次のとおりです。

package net.biancheng.c.interpreter;

import com.singularsys.jep.*;

public class JepDemo {
    
    
    public static void main(String[] args) throws JepException {
    
    
        Jep jep = new Jep();
        //定义要计算的数据表达式
        String 存款利息 = "本金*利率*时间";
        //给相关变量赋值
        jep.addVariable("本金", 10000);
        jep.addVariable("利率", 0.038);
        jep.addVariable("时间", 2);
        jep.parse(存款利息);    //解析表达式
        Object accrual = jep.evaluate();    //计算
        System.out.println("存款利息:" + accrual);
    }
}

預金利息:760.0

おすすめ

転載: blog.csdn.net/saienenen/article/details/112707206