Interpreter 패턴
이번 패턴은 도스나 리눅스에서 명령어를 치게되는데
shell> ls –a
이런식으로 치죠?
이때 shell>은 무시하고 ls는 명령어고 –a는 옵션이란 것을 인식하게됩니다.
이런식으로 여러 클래스를 만들어서 shell>만 인식하는 클래스 ls만 인식하는 클래스 –a만 인식하는 클래스는 만들어서 각자 할 수 있는 것만 처리하게 해봅시다.
여기서 나오는 명령어는 자동차를 움직입니다.
program go end 이런식입니다.
program은 위의 shell>과 같이 그냥 시작을 알리는 것이고 go는 명령어 end는 끝을 알립니다.
만약
program repeat 4 go right end end 면
repeat 4는 4번 반복하라는 뜻이고 어떤 것이면 go right를 4번 반복하라는 뜻입니다.
그리고 첫번째 end는 repeat의 end이고 두번째는 program의 end입니다. c언어의 중괄호로 묶는거랑 같습니다.
이름 |
해설 |
Node |
구문 트리의 노트가 되는 클래스 |
ProgramNode |
program 에 대응하는 클래스 |
CommandListNode |
command list에 대응하는 클래스 |
CommandNode |
command에 대응하는 클래스 |
RepeatCommandNode |
repeat command에 대응하는 클래스 |
PrimitiveCommandNode |
primitive command에 대응하는 클래스 |
Context |
구문해석을 위한 전후 관계를 나타내는 클래스 |
ParseException |
구문해석 중의 예외 클래스 |
Main |
동작 테스트용 클래스 |
다음을 실행하기 위해서는
위와같은 program.txt파일을 만들어야 합니다.
Node 클래스
package exam.kch;
public abstract class Node {
public abstract void parse(Context context)throws ParseException;
//parse메소드의 의미는 program go right 같은 것을 읽는다는 의미입니다.
}
ProgramNode 클래스
package exam.kch;
public class ProgramNode extends Node{
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
// TODO Auto-generated method stub
context.skipToken("program");//program이란 명령어는 skip 함.
commandListNode = new CommandListNode();//그 다음 명령어리스트 처리를 위해서 인스턴스를 만들고
commandListNode.parse(context);//context도 같이 넘겨줌.
}
public String toString(){
return "[program " + commandListNode + "]";//여기서 commandListNode.toString()이 불려짐
//이 메소드는 CommandListNode에 있음
}
}
CommandListNode 클래스
package exam.kch;
import java.util.ArrayList;
public class CommandListNode extends Node{
private ArrayList list = new ArrayList();
@Override
public void parse(Context context) throws ParseException {
// TODO Auto-generated method stub
while(true){
if(context.currentToken() == null){
throw new ParseException("Missing 'end'");
}else if(context.currentToken().equals("end")){//여기가 끝나는점 여기때문에 무한루프가 안걸림.
context.skipToken("end");
break;
}else{
//commandNodeList 다음에 commandNode로 넘어야됨
//명령어리스트 다음에는 명령어이므로.
Node commandNode = new CommandNode();
commandNode.parse(context);
list.add(commandNode);
}
}
}
public String toString(){
return list.toString();
}
}
CommandNode 클래스
package exam.kch;
public class CommandNode extends Node{
private Node node;
public void parse(Context context) throws ParseException{
//여기서는 명령어가 repeat 커맨드인지 아닌지를 구분함.
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 클래스
package exam.kch;
public class RepeatCommandNode extends Node{
private int number;
private Node commandListNode;
@Override
public void parse(Context context) throws ParseException {
// TODO Auto-generated method stub
//repeat 명령어의 경우는 repeat 3 이런식으로 쓰므로 3을 추출해야됨
context.skipToken("repeat");
number = context.currentNumber();//3추출
context.nextToken();//다음것을 가리킴
commandListNode = new CommandListNode();//CommandListNode로 넘겨줌
commandListNode.parse(context);
}
public String toString(){
return "[repeat " + number + " " + commandListNode +"]";
}
}
PrimitiveCommandNode 클래스
package exam.kch;
public class PrimitiveCommandNode extends Node{
private String name;
public void parse(Context context) throws ParseException{
//repeat 명령어가 아니면 여기를 오게됨.
name = context.currentToken();//현재 명령어 받아오고
context.skipToken(name);//다음 명령어 가리킴
//go, right, left가 아니면 예외발생
if(!name.equals("go")&& !name.equals("right") && !name.equals("left")){
throw new ParseException(name + " is undefined");
}
}
public String toString(){
return name;
}
}
Context 클래스
package exam.kch;
import java.util.StringTokenizer;
public class Context {
//StringTokenizer 클래스는 공백, 탭, 줄바꿈, 캐리지 리턴, 폼피트로 구분을 해서 토큰(단어)으로 나눠줌.
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) throws ParseException{
//없다면 예외발생
if(!token.equals(currentToken)){
throw new ParseException("Warning : " + token + " is expected, but " + currentToken + " is found.");
}
//다음것을 가리킴
nextToken();
}
//현재 숫자를 가져옴.
public int currentNumber() throws ParseException{
int number = 0;
try{
number = Integer.parseInt(currentToken);
}catch(NumberFormatException e){
throw new ParseException("Warning: " + e);
}
return number;
}
}
ParseException 클래스
package exam.kch;
public class ParseException extends Exception{
public ParseException(String msg){
super(msg);
}
}
Main 클래스
package exam.kch;
import java.io.BufferedReader;
import java.io.FileReader;
public class Main {
public static void main(String[] args){
try{
BufferedReader reader = new BufferedReader(new FileReader("program.txt"));
String text;
while((text = reader.readLine()) != null){
System.out.println("text = \"" + text + "\"");
Node node = new ProgramNode();//program이란 단어를 스킵하기 위해서 처음에 program 인스턴스를 만듬
node.parse(new Context(text));//이때 skip하거나 next 명령어를 처리하는 것은 Context클래스이므로 인스턴스를 넘겨줌
//여기서 text는 한줄 읽은 명령어임
System.out.println("node = " + node);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
간단하게 명령어를 입력하면 각 클래스가 자기가 맡은 부분만 처리하고 다른 부분은 다른 클래스로 넘기게 됩니다. 또한 새로운 명령어가 나오면 새로운 클래스를 생성하기만 하면됩니다.
'프로그래밍 > 자바 디자인패턴' 카테고리의 다른 글
디자인패턴 Command 패턴 (1) | 2012.08.18 |
---|---|
디자인패턴 Proxy 패턴 (0) | 2012.08.16 |
디자인패턴 Flyweight 패턴 (0) | 2012.08.15 |
디자인패턴 State 패턴 (0) | 2012.08.14 |
디자인 패턴 Memento 패턴 (0) | 2012.08.13 |
디자인패턴 Observer 패턴 (0) | 2012.08.10 |
디자인패턴 Mediator 패턴 (0) | 2012.08.09 |
디자인패턴 Facade 패턴 (0) | 2012.08.06 |