`

Design Pattern: Interpreter 模式

阅读更多

对于一个具有层次节点关系的问题来说,如果您要剖析每一个节点,您可以使用Interpreter模式,直译器模式有些类似演算法中的个别击破方式,对每一个父节点我们剖析出其子节点组合,然而交给子节点剖析物件继续剖析,直到剖析至终端节点为止。

举个例子来说明好了,先说明的是,这个例子是改写自 Design Patterns于Java语言之实习应用 第23章的范例,我将之更简化了,以让大家将焦点能集中在如何使用Interpreter模式,以及如何实用。

假设您要实作一个Interpreter,这个Interpreter可以直译您文字档中的程式,并依您自订的程式文法来执行程式,几个简单的程式如下:

PROGRAM
    PRINT dog SPACE
    PRINT is SPACE
    PRINT an SPACE
    PRINT animai
END

 
您的这式程个会印出"dog is an animal"的文字,再来一个例子是:

PROGRAM
    REPEAT 2
        LINEBREAK
        PRINT dog
        BREAK
    END
END

 

这个程式要印出:

------------------------------
 dog
------------------------------
 dog


您也可以任意的组合程式,例如:

PROGRAM
    PRINT begin
    BREAK
    REPEAT 3
        REPEAT 2
            PRINT dog SPACE
            PRINT is SPACE
            PRINT a SPACE
            PRINT animal
            BREAK
        END
    END
END

 

这个程式中的几个关键字是PROGRAM、PRINT、SPACE、BREAK、LINEBREAK、REPEAT、END, PROGRAM是表示程式开始,以END作结,PRINT可以印出一个无空白的字串,SPACE印出一个空白,BREAK是换行,而LINEBREAK是画一个直线并换行,REPEAT是回圈指令,可以指定回圈次数,以END作结。

观察程式,可以制定出以下的文法,如下:

<program> ::= PROGRAM <command list>
<command list> ::= <command>* END
<command> ::= <repeat command> | <primitive command>
<repeat command> ::= REPEAT <number> <command list>
<primitive command> ::= PRINT <string>
                         | BREAK | SPACE | LINEBREAK

 

程式文法制定需要对程式进行语句分析与定义,在这边并不讨论这个课题,在程式中,command节点由primitive或repeat两个节点任意组合,一个command list节点则是零个以上的command节点组合而成,其中repeat还可以组合command list节点,这是组合模式的应用,可以在程式中组合巢状回圈。

在直译程式时,以读到PROGRAM作为开始节点,接下来我们剖析程式为command list 节点,并将它们丢给专门剖析command list的物件继续剖析,这个物件将之分析,看是不是有repeat command或primitive command节点,如果有就再往下交由专属物件进行剖析,如此层层剥开,并由专属物件负责剖析工作。

Interpreter模式的基本观念就如上所示,先来看看如何以程式实现剖析的过程,下面这个程式会剖析您的程式,并将程式加上对应的括号来将同一个区块组合起来,以表示它完成剖析之后的结果:

  • INode.java
public interface INode { 
    public void parse(Context context); 
} 

 

  • ProgramNode.java
// <program> ::= PROGRAM <command list> 
public class ProgramNode implements INode { 
    private INode commandListNode; 
    public void parse(Context context) { 
        context.skipToken("PROGRAM"); 
        commandListNode = new CommandListNode(); 
        commandListNode.parse(context); 
    } 

    public String toString() { 
        return "[PROGRAM " + commandListNode + "]"; 
    } 
}  

 

  • CommandListNode.java
import java.util.Vector; 

// <command list> ::= <command>* END 
public class CommandListNode implements INode { 
    private Vector list = new Vector();

    public void parse(Context context) { 
        while (true) { 
            if (context.currentToken() == null) { 
                System.err.println("Missing 'END'"); 
                 break; 
            } else if (
                    context.currentToken().equals("END")) { 
                context.skipToken("END"); 
                break; 
            } else { 
                INode commandNode = new CommandNode(); 
                commandNode.parse(context); 
                list.add(commandNode); 
            } 
        } 
    }

    public String toString() { 
        return "" + list; 
    } 
}  

 

  • CommandNode.java
// <command> ::= <repeat command> | <primitive command> 
public class CommandNode implements INode { 
    private INode node;

    public void parse(Context context) { 
        if (context.currentToken().equals("REPEAT")) { 
            node = new RepeatCommandNode(); 
            node.parse(context); 
        } else { 
            node = new PrimitiveCommandNode(); 
            node.parse(context); 
        } 
    }

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

 

  • RepeatCommandNode.java
public class RepeatCommandNode implements INode { 
    private int number; 
    private INode commandListNode; 

    public void parse(Context context) { 
        context.skipToken("REPEAT"); 
        number = context.currentNumber(); 
        context.nextToken(); 
        commandListNode = new CommandListNode(); 
        commandListNode.parse(context); 
    }

    public String toString() {
        return "[REPEAT " + number + " " 
                  + commandListNode + "]"; 
    } 
}

 

  • PrimitiveCommandNode.java
// <primitive command> ::= PRINT <string> 
//                           | SPACE | BREAK | LINEBREAK 
public class PrimitiveCommandNode implements INode {
    private String name;
    private String text;

    public void parse(Context context) { 
        name = context.currentToken(); 
        context.skipToken(name); 
        if (!name.equals("PRINT") && !name.equals("BREAK") 
             && !name.equals("LINEBREAK") 
             && !name.equals("SPACE")) { 
            System.err.println("Undefined Command"); 
        }

        if (name.equals("PRINT")) { 
            text = context.currentToken(); 
            name += text; 
            context.nextToken(); 
        } 
    }

    public String toString() { 
        return name; 
    } 
}  

 

  • Context.java
import java.util.*; 

public class Context { 
    private StringTokenizer tokenizer; 
    private String currentToken; 

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

    public String nextToken() { 
        if (tokenizer.hasMoreTokens()) { 
            currentToken = tokenizer.nextToken(); 
        } else { 
            currentToken = null; 
        } 
        return currentToken; 
    } 

    public String currentToken() { 
        return currentToken; 
    } 

    public void skipToken(String token) { 
        if (!token.equals(currentToken)) { 
            System.err.println("Warning: " + token + 
                          " is expected, but " + 
                          currentToken + " is found."); 
        } 
        nextToken(); 
    } 

    public int currentNumber() { 
        int number = 0; 
        try { 
            number = Integer.parseInt(currentToken); 
        } catch (NumberFormatException e) { 
            System.err.println("Warning: " + e); 
        } 
        return number; 
    } 
}  

 

  • Main.java
import java.util.*; 
import java.io.*;

public class Main { 
    public static void main(String[] args) { 
        try { 
            BufferedReader reader = new 
                  BufferedReader(new FileReader(args[0])); 
            String text; 
            while ((text = reader.readLine()) != null) { 
                System.out.println("text = \"" + 
                                       text + "\""); 
                INode node = new ProgramNode(); 
                node.parse(new Context(text)); 
                System.out.println("node = " + node); 
            } 
        } 
        catch (ArrayIndexOutOfBoundsException e) { 
            System.err.println(
                   "Usage: java Main yourprogram.txt"); 
        } 
        catch (Exception e) { 
            e.printStackTrace(); 
        } 
    } 
} 


假设您的程式是这样写的:

  • program.txt
PROGRAM PRINT xxx END
PROGRAM REPEAT 4 PRINT xxx END END 
PROGRAM REPEAT 4 PRINT xxx PRINT "yyy" END END


则执行Intrepreter程式之后会是:

 $ java Main program.txt
 text = "PROGRAM PRINT xxx END"
 node = [PROGRAM [PRINTxxx]]

 text = "PROGRAM REPEAT 4 PRINT xxx END END"
 node = [PROGRAM [[REPEAT 4 [PRINTxxx]]]]

 text = "PROGRAM REPEAT 4 PRINT xxx PRINT "yyy" END END"
 node = [PROGRAM [[REPEAT 4 [PRINTxxx, PRINT"yyy"]]]]

 

TerminalExpression就像我们的primitive command,再剖析下去已经没有子节点了,而NonterminalExpression就像是repeat command,注意到其中也使用了组合模式,就如之前所说的,组合模式让可以递回的组合句子为更复杂的语句。

您已经会剖析句子了,接下来要如何让这个直译器真正工作,虽然程式中使用toString()来表示每一个节点的剖析结果,但事实上,这个程式也已经说明了如何让剖析的结果真正运作了,既然已经记录好剖析之后的语句顺序了,只要由上而下追踪剖析结果,就一定可以执行到 primitive command,且顺序符合自订的程式原始码的需求,这只要将toString()改为execute(),并作一些转发与重复执行的修改就可以了,直接来看程式会比较容易理解:

  • INode.java
public interface INode {
    public void parse(Context context);
    public void execute();
} 

 

  • ProgramNode.java
// <program> ::= PROGRAM <command list>
public class ProgramNode implements INode {
    private INode commandListNode;

    public void parse(Context context) {
        context.skipToken("PROGRAM");
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    public void execute() {
        commandListNode.execute();
    }

    public String toString() {
        return "[PROGRAM " + commandListNode + "]";
    }
} 

 

  • CommandListNode.java
import java.util.*;    

// <command list> ::= <command>* END
public class CommandListNode implements INode {
    private Vector list = new Vector();
    private INode commandNode;

    public void parse(Context context) {
        while (true) {
            if (context.currentToken() == null) {
                System.err.println("Missing 'END'");
                break;
            } else if(context.currentToken().equals("END")) {
                context.skipToken("END");
                break;
            } else {
                commandNode = new CommandNode();
                commandNode.parse(context);
                list.add(commandNode);
            }
        }
    }

    public void execute() {
        Iterator it = list.iterator();
        while (it.hasNext()) {
            ((CommandNode)it.next()).execute();
        }
    }

    public String toString() {
        return "" + list;
   }
} 

 

  • CommandNode.java
// <command> ::= <repeat command> | <primitive command>
public class CommandNode implements INode {
    private INode node;

    public void parse(Context context) {
        if (context.currentToken().equals("REPEAT")) {
            node = new RepeatCommandNode();
            node.parse(context);
        } else {
            node = new PrimitiveCommandNode();
            node.parse(context);
        }
    }

    public void execute() {
        node.execute();
    }

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

 

  • PrimitiveCommandNode.java
// <primitive command> ::= PRINT <string> 
//                           | SPACE | BREAK | LINEBREAK
public class PrimitiveCommandNode implements INode {
    private String name;
    private String text;

    public void parse(Context context) {
        name = context.currentToken();
        context.skipToken(name);
        if (!name.equals("PRINT") && !name.equals("BREAK") 
                           && !name.equals("LINEBREAK") 
                           && !name.equals("SPACE")) {
            System.err.println("Undefined Command");
        }

        if (name.equals("PRINT")) {
            text = context.currentToken();
            context.nextToken();
        }
    } 

    public void execute() {
        if(name.equals("PRINT"))
            System.out.print(text);
        else if(name.equals("SPACE"))
            System.out.print(" ");
        else if(name.equals("BREAK"))
            System.out.println();
        else if(name.equals("LINEBREAK"))
            System.out.println(
                  "\n------------------------------");
    }

    public String toString() {
        return name;
    }
} 

 

  • RepeatCommandNode.java
public class RepeatCommandNode implements INode {
    private int number;
    private INode commandListNode;

    public void parse(Context context) {
        context.skipToken("REPEAT");
        number = context.currentNumber();
        context.nextToken();
        commandListNode = new CommandListNode();
        commandListNode.parse(context);
    }

    public void execute() {
        for(int i = 0; i < number; i++)
            commandListNode.execute();
    }

    public String toString() {
        return "[REPEAT " + number + " " + 
                             commandListNode + "]";
    }
} 

 

  • Context.java
import java.util.*;

public class Context {
    private StringTokenizer tokenizer;
    private String currentToken;

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

    public String nextToken() {
        if (tokenizer.hasMoreTokens()) {
            currentToken = tokenizer.nextToken();
        } else {
            currentToken = null;
        }
        return currentToken;
    }

    public String currentToken() {
        return currentToken;
    }

    public void skipToken(String token) {
        if (!token.equals(currentToken)) {
            System.err.println("Warning: " + token + 
                           " is expected, but " + 
                           currentToken + " is found.");
        }
        nextToken();
    }

    public int currentNumber() {
        int number = 0;
        try {
            number = Integer.parseInt(currentToken);
        } catch (NumberFormatException e) {
            System.err.println("Warning: " + e);
        }
        return number;
    }
} 

 

  • Main.java
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            BufferedReader reader = new BufferedReader(
                               new FileReader(args[0]));
            String text;
            while ((text = reader.readLine()) != null) {
                System.out.println("text = \"" + text 
                                      + "\"");
                INode node = new ProgramNode();
                node.parse(new Context(text));
                node.execute();
            }
        }
        catch (ArrayIndexOutOfBoundsException e) {
            System.err.println(
                 "Useage: java Main yourprogram.txt");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}


假设您的直译程式稿是这么撰写的:

  • program.txt
PROGRAM REPEAT 4 LINEBREAK PRINT justin SPACE PRINT momor LINEBREAK END END


则程式执行的结果就是:

  $ java Main program.txt
 text = "PROGRAM REPEAT 4 LINEBREAK PRINT justin SPACE
         PRINT momor LINEBREAK END END"
 ------------------------------
 justin momor
 ------------------------------

 ------------------------------
 justin momor
 ------------------------------

 ------------------------------
 justin momor
 ------------------------------

 ------------------------------
 justin momor
 ------------------------------ 


Design Patterns于Java语言之实习应用 第23章的范例中,可以让您依指令稿直译,画出任何的图案,让范例结合了工厂(Factory)模式、外观(Facade)模式等等,在这边建议您看看那个范例,看看不同的设计模式之间如何组合且相互合作。

 

分享到:
评论

相关推荐

    DesignPattern::pencil:设计模式_java实现以及详解

    本资源“DesignPattern::pencil:设计模式_java实现以及详解”提供了一套详细的学习材料,帮助开发者理解和应用设计模式。 该资源的作者是“养码青年-Style”,他通过这个项目记录了自己的设计模式学习过程。鼓励...

    DesignPattern:设计模式

    DesignPattern-master这个压缩包可能包含了一个关于设计模式的项目或者教程资源。 设计模式分为三类:创建型模式(Creational Patterns)、结构型模式(Structural Patterns)和行为型模式(Behavioral Patterns)...

    designPattern:设计模式相关代码实现

    "designPattern:设计模式相关代码实现"这个项目,显然提供了不同设计模式在Java语言中的实际应用示例。 在Java世界里,设计模式主要分为三大类:创建型模式、结构型模式和行为型模式。每种模式都针对特定的编程问题...

    DesignPattern:设计模式.net源代码

    本资源"DesignPattern:设计模式.net源代码"提供了一套基于.NET实现的设计模式示例,旨在帮助程序员更好地理解和应用这些模式。 在"DesignPattern-master"这个压缩包中,你可能找到的文件结构和内容包括: 1. **...

    DesignPattern:设计模式演示程序

    这个名为"DesignPattern"的压缩包文件很可能包含了一个Java实现的各种设计模式的示例程序。 在这个"DesignPattern-master"目录中,我们可以期待找到一系列与设计模式相关的Java源代码文件(.java),每个文件或...

    C++设计模式(Design Pattern)范例源代码

    23种设计模式(Design Pattern)的C++实现范例,包括下面列出的各种模式,代码包含较详细注释。另外附上“设计模式迷你手册.chm”供参考。 注:项目在 VS2008 下使用。 创建型: 抽象工厂模式(Abstract Factory) 生成...

    DesignPattern:C#设计模式示例

    "DesignPattern:C#设计模式示例"这个资源很可能是包含多个C#实现的设计模式示例代码库。 设计模式通常分为三类:创建型、结构型和行为型。每种模式都解决了特定场景下的问题,并提供了良好的代码组织和扩展性。 ...

    Example-Design-Pattern:设计模式示例的存储库

    示例设计模式设计模式示例的存储库。 请参阅示例代码信息(默认程序包) /DesignPattern/src/Main.java 切入... /DesignPattern/src/designpattern/behavioral/Interpreter.java 解析命令行。 /DesignPattern/src/desig

    DesignPattern:设计模式的学习笔记和示例代码

    设计模式是软件工程中的一种最佳实践,它是在特定情境下解决常见问题的经验总结。...在DesignPattern-master这个压缩包中,你可以找到关于这些模式的详细讲解和实例代码,为你的Java开发之旅提供宝贵的参考资料。

    Design-pattern:设计模式

    这个名为"Design-pattern:设计模式"的压缩包可能包含了一个关于设计模式的学习资源,比如代码示例或者教程。 创建型设计模式是设计模式中的一个类别,主要关注对象的创建过程。这类模式包括工厂方法(Factory ...

    JAVA 23种设计模式(全).Design Pattern_Java模式

    本资料包“JAVA 23种设计模式(全).Design Pattern_Java模式”涵盖了所有23种经典的GOF(GoF,Gang of Four)设计模式,旨在帮助开发者深入理解和应用这些模式。 首先,我们来看一下23种设计模式的分类: 1. **创建...

    designpattern:PHP设计模式教程

    本教程的"designpattern-master"可能包含以上各种设计模式的实例代码和详细解释,帮助学习者通过实践掌握这些模式。在实际项目中,灵活运用这些设计模式可以提高代码质量,使项目更加健壮和易于维护。对于PHP开发者...

    design-pattern:设计模式学习

    "design-pattern:设计模式学习"这个主题涵盖了许多关键知识点,包括但不限于以下几个方面: 1. **单例模式(Singleton)**:保证一个类只有一个实例,并提供全局访问点。在Java中,可以通过双重检查锁定、静态内部...

    design pattern PPT

    设计模式(Design Pattern)是软件工程中用于解决软件设计问题的既定方案。设计模式并非直接的代码实现,而是一套被验证过的通用解决方案模板。它们是由经验丰富的软件开发者总结出的,在特定上下文中对常见问题的...

    Design Pattern英文版

    设计模式(Design Pattern)是软件工程中的一种经验总结,它是在特定上下文中为解决常见问题而提出的一套可复用的解决方案。设计模式并不直接实现为代码,而是提供了一种在面向对象设计中如何处理常见问题的指南。...

    design pattern设计模式范例

    本资料库中的"design pattern设计模式范例"提供了23种经典设计模式以及最新的范式用法,非常适合用于项目重构或作为学习设计模式的参考。 首先,我们来看23种经典的设计模式,它们通常分为三类:创建型、结构型和...

    阅读java源码-JavaDesignPattern:23种设计模式Java实现

    在“阅读java源码-JavaDesignPattern:23种设计模式Java实现”中,我们将深入探讨这23种设计模式的Java实现。 1. **创建型模式**(Creational Patterns): - **单例模式(Singleton)**:确保一个类只有一个实例,...

    设计模式java源码-design-pattern:java设计模式源代码

    这个名为"design-pattern:java设计模式源代码"的压缩包文件很可能是包含了多种Java实现的设计模式示例。 首先,我们来看看主要的设计模式分类: 1. 创建型模式:这类模式涉及到对象的创建,如单例模式(Singleton...

Global site tag (gtag.js) - Google Analytics