1. 程式人生 > >設計模式的藝術 行為型模式之直譯器模式

設計模式的藝術 行為型模式之直譯器模式

前言

目前計算器程式語言有好幾百種,但是有時候人們還是希望能用一些簡單的語言表達實現一些特定的操作,比如輸入一個檔案,它就可以按照預定的格式進行解釋,從而實現相應的功能。

在現實的開發中,這些簡單的自定義語言可以通過現有的程式語言來設計,如果所基於的程式語言是面嚮物件語言,此時可以使用直譯器模式來實現自定義語言

什麼是直譯器模式 Interpreter Pattern

定義一個語言的方法,並且建立一個直譯器來解釋該語言中的句子,這裡的"語言"是指使用規定和語言的程式碼。直譯器模式是一種物件行為型模式

直譯器模式的優點

(1)、易於改變和擴充套件文法,由於直譯器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制改變和擴充套件文法。

(2)、每一條文法規則都可以表示一個類,因此可以方便地實現一個簡單的語言。

(3)、實現文法比較容易,在抽象語法樹中每一個表示式節點類的實現方式都是相似的,這些類的程式碼編寫都不會特別複雜,還可以通過一些工具自動生成節點類程式碼

(4)、增加新的解釋表示式比較方便,如果使用者需要增加新的解釋表示式只需要對應增加一個新的終結符表示式或非終結符表示式類,原有表示式類程式碼無須修改,符合開閉原則

直譯器模式的缺點

(1)、對於複雜文法難以維護,在直譯器模式中,每一個規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數會急劇增加,導致系統難以維護和管理,此時可以考慮使用語法分析程式等方式來取代直譯器模式

(2)、執行效率低。由於在直譯器模式總使用了大量的迴圈和遞迴呼叫,因此在直譯器較為複雜的句子時速度很慢,而且程式碼除錯過程也比較麻煩

直譯器模式適用的場景

(1)、可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹

(2)、一些重複出現的問題可以用一種簡單的語言來進行表達

(3)、一個語言的中文法較為簡單

(4)、當執行效率不是關鍵問題時(畢竟直譯器模式的執行效率並不高)

直譯器模式的具體實現

目錄結構(模擬機器人指令)

抽象基類

//本例項對機器人控制指令的輸出結果進行模擬,將英文指令翻譯為中文指令,實際情況是呼叫
//不同的控制程式進行機器人的控制,包括對移動方向、方式和距離的控制等
public abstract class AbstractNode {
    public abstract String interpret();
}

具體類

package com.company.node;

//動作解釋:終結符表示式
public class ActionNode extends AbstractNode {
    private String action;

    public ActionNode(String action) {
        this.action = action;
    }

    @Override
    //動作移動方式表示式的解釋操作
    public String interpret() {
        if(action.equalsIgnoreCase("move")){
            return "移動";
        }else  if(action.equalsIgnoreCase("run")){
            return  "快速移動";
        }else {
            return "無效指令";
        }
    }
}
package com.company.node;

import com.company.node.AbstractNode;

//And解釋:非終結符表示式
public class AndNode extends AbstractNode {
    private AbstractNode left;  //And的左表示式
    private AbstractNode right; //ANd的右表示式

    public AndNode(AbstractNode left, AbstractNode right) {
        this.left = left;
        this.right = right;
    }

    @Override
    //And表示式解釋操作
    public String interpret() {
        return left.interpret()+"再"+right.interpret();
    }
}
package com.company.node;

import com.company.node.AbstractNode;

//方向解釋:終結符表示式
public class DirectionNode extends AbstractNode {
    private String direction;

    public DirectionNode(String direction) {
        this.direction = direction;
    }

    @Override
    //方向表示式的解釋操作
    public String interpret() {
        if(direction.equalsIgnoreCase("up")){
            return "向上";
        }else  if(direction.equalsIgnoreCase("down")){
            return "向下";
        }else if(direction.equalsIgnoreCase("left")){
            return "向左";
        }else if(direction.equalsIgnoreCase("right")){
            return "向右";
        }else {
            return "無效指令";
        }
    }
}
package com.company.node;

import com.company.node.AbstractNode;

//距離解釋:終結符表示式
public class DistanceNode extends AbstractNode {
    private String distance;

    public DistanceNode(String distance) {
        this.distance = distance;
    }

    @Override
    //距離表示式的解釋操作
    public String interpret() {
        return this.distance;
    }
}
package com.company.node;

import com.company.node.AbstractNode;

//簡答句子解釋:非終結符表示式
public class SentCeNode extends AbstractNode {
    private AbstractNode direction;
    private AbstractNode action;
    private AbstractNode distance;

    public SentCeNode(AbstractNode direction, AbstractNode action, AbstractNode distance) {
        this.direction = direction;
        this.action = action;
        this.distance = distance;
    }

    @Override
    //簡單句子的解釋操作
    public String interpret() {
        return direction.interpret()+action.interpret()+distance.interpret();
    }
}

指令處理工具類

package com.company;

import com.company.node.*;

import java.util.Stack;

//指令處理類:工具類
public class InstructionHandler {
    private AbstractNode node;
    public void handle(String instruction){
        AbstractNode left=null,right=null;
        AbstractNode direction=null,action=null,distance=null;
        Stack stack=new Stack();  //宣告一個棧物件用於儲存抽象語法樹
        String[]words=instruction.split(" ");  //以空格分隔指定字串
        for(int i=0;i<words.length;i++){
            //本例項採用棧的方式類處理指令,如果遇到"and",則將其後的3個單詞作為3個終結符
            //表示式連成一個一個簡單的句子SentceNode作為"and"的右表示式,而將從棧頂彈出的表示式
            //作為"and"的左表示式,最後將新的"and"表示式壓入棧中。
            if(words[i].equalsIgnoreCase("and")){
                left=(AbstractNode) stack.pop();  //彈出棧頂表示式作為左表示式
                String word1=words[++i];
                direction=new DirectionNode(word1);
                String word2=words[++i];
                action=new ActionNode(word2);
                String word3=words[++i];
                distance=new DistanceNode(word3);
                right=new SentCeNode(direction,action,distance);  //右表示式
                stack.push(new AndNode(left,right));  //將新表示式壓入棧中
            }
            //如果是從頭開始進行解釋,則將前三個單片語成一個簡單的句子SentenceNode並將
            //該句子壓入棧中
            else {
                String word1=words[i];
                direction=new DistanceNode(word1);
                String word2=words[++i];
                action=new ActionNode(word2);
                String word3=words[++i];
                distance=new DistanceNode(word3);
                left=new SentCeNode(direction,action,distance);
                stack.push(left);
            }
        }
        this.node=(AbstractNode) stack.pop(); //將全部表示式從棧中彈出
    }
    public String output(){
        String result=node.interpret();  //解釋表示式
        return  result;
    }
}

客戶端測試類

package com.company;

public class Client {

    public static void main(String[] args) {
        String instruction ="up move 5 and down run 10 and left move 5";
        InstructionHandler handler = new InstructionHandler();
        handler.handle(instruction);
        String outString;
        outString = handler.output();
        System.out.println(outString);
    }
}