設計模式學習筆記——解釋器(Interpreter)模式

設計模式學習筆記——解釋器(Interpreter)模式

@(設計模式)[設計模式, 解釋器模式, Interpreter]

基本介紹

給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

解釋器案例

類圖

解釋器案例類圖

實現代碼

Node抽象類

package com.pc.interpreter.example;

/**
 * 節點類
 */
public abstract class Node {
    /**
     * 解析
     *
     * @param context 上下文
     * @throws ParseException 解析異常
     */
    public abstract void parse(Context context) throws ParseException;
}

ParseException類

package com.pc.interpreter.example;

/**
 * 解析異常類
 */
public class ParseException extends Exception {
    public ParseException(String msg) {
        super(msg);
    }
}

ProgramNode類

package com.pc.interpreter.example;

/**
 * 程序節點類
 * <program> ::= program <command list>
 */
public class ProgramNode extends Node {
    /**
     * 命令列表節點
     */
    private Node commandListNode;

    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("program");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    @Override
    public String toString() {
        return "[program " + commandListNode + "]";
    }
}

CommandListNode類

package com.pc.interpreter.example;

import java.util.ArrayList;

/**
 * 命令列表節點類
 * <command list> ::= <command>* end
 */
public class CommandListNode extends Node {
    /**
     * 命令列表
     */
    private ArrayList list = new ArrayList();

    @Override
    public void parse(Context context) throws ParseException {
        while (true) {
            if (context.currentToken() == null) {
                throw new ParseException("Missing 'end'");
            } else if (context.currentToken().equals("end")) {
                context.skipToken("end");
                break;
            } else {
                Node commandNode = new CommandNode();
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }

    @Override
    public String toString() {
        return list.toString();
    }
}

CommandNode類

package com.pc.interpreter.example;

/**
 * 命令節點類
 * <command> ::= <repeat command> | <primitive command>
 */
public class CommandNode extends Node {
    /**
     * 節點
     */
    private Node node;

    @Override
    public void parse(Context context) throws ParseException {
        if (context.currentToken().equals("repeat")) {
            node = new RepeatCommandNode();
            node.parse(context);
        } else {
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }

    @Override
    public String toString() {
        return node.toString();
    }
}

RepeatCommandNode類

package com.pc.interpreter.example;

/**
 * 重複命令節點類
 * <repeat command> ::= repeat <number> <command list>
 */
public class RepeatCommandNode extends Node {
    /**
     * 重複次數
     */
    private int number;
    /**
     * 命令列表節點
     */
    private Node commandListNode;

    @Override
    public void parse(Context context) throws ParseException {
        context.skipToken("repeat");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    @Override
    public String toString() {
        return "[repeat " + number + " " + commandListNode + "]";
    }
}

PrimitiveCommandNode類

package com.pc.interpreter.example;

/**
 * 原始命令節點類
 * <primitive command> ::= go | right | left
 */
public class PrimitiveCommandNode extends Node {
    /**
     * 名字
     */
    private String name;

    @Override
    public void parse(Context context) throws ParseException {
        name = context.currentToken();
        context.skipToken(name);
        if (!name.equals("go") && !name.equals("right") && !name.equals("left")) {
            throw new ParseException(name + " is undefined");
        }
    }

    @Override
    public String toString() {
        return name;
    }
}

Context類

package com.pc.interpreter.example;


import java.util.StringTokenizer;

/**
 * 上下文類
 */
public class Context {
    /**
     * 分詞器
     */
    private StringTokenizer tokenizer;
    /**
     * 當前符號
     */
    private String currentToken;

    public Context(String text) {
        this.tokenizer = new StringTokenizer(text);
        this.nextToken();
    }

    /**
     * 返回下一個符號
     *
     * @return 下一個符號
     */
    public String nextToken() {
        if (tokenizer.hasMoreTokens()) {
            this.currentToken = this.tokenizer.nextToken();
        } else {
            this.currentToken = null;
        }
        return this.currentToken;
    }

    /**
     * 返回當前符號
     *
     * @return 當前符號
     */
    public String currentToken() {
        return this.currentToken;
    }

    /**
     * 跳過指定符號
     *
     * @param token 指定符號
     * @throws ParseException 解析異常
     */
    public void skipToken(String token) throws ParseException {
        if (!token.equals(this.currentToken)) {
            throw new ParseException("Warning: " + token + " is expected, but " + currentToken + " is found.");
        }
        this.nextToken();
    }

    /**
     * 解析數值
     *
     * @return 數值
     * @throws ParseException 解析異常
     */
    public int currentNumber() throws ParseException {
        int number = 0;
        try {
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
            throw new ParseException("Warning: " + e);
        }
        return number;
    }
}

測試類

package com.pc.interpreter.example.test;

import com.pc.interpreter.example.Context;
import com.pc.interpreter.example.Node;
import com.pc.interpreter.example.ProgramNode;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.FileReader;

/**
 * Interpreter Tester.
 *
 * @author Switch
 * @version 1.0
 */
public class InterpreterTest {
    /**
     * 測試解釋器模式
     */
    @Test
    public void testInterpreter() {
        try {
            BufferedReader reader = new BufferedReader(new FileReader(System.getProperty("user.dir")
                    + "/src/main/java/com/pc/interpreter/example/program.txt"));
            String text;
            while ((text = reader.readLine()) != null) {
                System.out.println("text = \"" + text + "\"");
                Node node = new ProgramNode();
                node.parse(new Context(text));
                System.out.println("node = " + node);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

運行結果

text = "program end"
node = [program []]
text = "program go end"
node = [program [go]]
text = "program go right go right go right go right end"
node = [program [go, right, go, right, go, right, go, right]]
text = "program repeat 4 go right end end"
node = [program [[repeat 4 [go, right]]]]
text = "program repeat 4 repeat 3 go right go left end right end end"
node = [program [[repeat 4 [[repeat 3 [go, right, go, left]], right]]]]

解釋器模式中的角色

AbstractExpression(抽象表達式)

AbstractExpression角色定義了語法樹節點的共同接口(API)。在案例中,由Node類扮演此角色。在案例中,共同接口(API)的名字是parse,不過在類圖中它的名字是interpreter

TerminalExpression(終結符表達式)

TerminalExpression角色對應BNF中的終結特表達式。在案例中,由PrimitiveCommandNode類扮演此角色。

NonterminalExpression(非終結符表達式)

NonterminalExpression角色對應BNF中的非終結符表達式。在案例中,由ProgramNode類、CommandNode類、RepeatCommandNode類和CommandListNode 類扮演此角色。

Context(文脈、上下文)

Context 角色爲解釋器進行語法解析提供了必要的信息。在案例中,由Context 類扮演此角色。

Client(請求者)

爲了推導語法樹, C li ent 角色會調用TerminalExpression 角色和Nonterm inalExpress i on 角色。在案例中,由Ma 工n 類扮演此角色。

類圖

解釋器模式類圖

GitHub:DesignPatternStudy

——————參考《圖解設計模式》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章