[Design Pattern] 인터프리터 패턴

Interpreter Pattern

인터프리터 패턴은 어떤 언어에 대해 그 언어의 문법에 대한 표현을 정의하면서 그것을 사용하여 해당 언어로 기술된 문장을 해석하는 해석자를 함께 정의하는 패턴이다.

활용성

정의할 언어의 문법이 간단할 때

문법이 복잡하다면 분법을 정의하는 클래스 계통이 복잡해진다. 이럴 때는 인터프리터 패턴보다 파서와 같은 모듈을 이용하는 것이 더 나은 방법이다.

인터프리터 패턴 사용에 따른 결과

  1. 문법의 변경과 확장이 쉽다.
  2. 문법의 구현이 용이하다.
  3. 복잡한 문법은 관리하기 어렵다.
  4. 표현식을 해석하는 새로운 방법을 추가할 수 있다.

인터프리터

구조

인터프리터의 구조는 다음과 같다.

Interpreter Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void InterpreterPattern::interpret()
{
Expression* expression = new IntegerConverterExpression();
std::string line1 = "Binary 1234";
std::string line2 = "Hex 1234";
std::string line3 = "Binary 1234";
std::string line4 = "Hex1234fakjwefakljhfawekljh";
std::string line5 = "What did i say?";
std::string line6 = "Hex 987654321";
std::cout<<expression->interpret(line1)<<std::endl;
std::cout<<expression->interpret(line2)<<std::endl;
std::cout<<expression->interpret(line3)<<std::endl;
std::cout<<expression->interpret(line4)<<std::endl;
std::cout<<expression->interpret(line5)<<std::endl;
std::cout<<expression->interpret(line6)<<std::endl;
}
1
2
3
4
5
class Expression
{
public:
virtual std::string interpret(std::string line) = 0;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class IntegerConverterExpression : public Expression
{
enum Support
{
HEX,
BINARY,
};

std::map<Support, std::pair<std::string, Expression*>> expressionMapper;

static const std::string UNSUPPORT;
public:
IntegerConverterExpression()
{
expressionMapper[BINARY] = std::make_pair("Binary", new BinaryExpression());
expressionMapper[HEX] = std::make_pair("Hex", new HexExpression());
}

std::string interpret(std::string line) override
{
for(auto enums : expressionMapper)
{
auto result = StringParser::contains(enums.second.first, line);
if(result.has_value())
return expressionMapper[enums.first].second->interpret(result.value());
}

return UNSUPPORT + line;
}
};

const std::string IntegerConverterExpression::UNSUPPORT = "Unsupported Expression : ";
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class HexExpression : public Expression
{
public:
std::string interpret(std::string line) override
{
int value = StringParser::getValue(line);
std::stringstream stream;
stream << std::hex << value;
return stream.str();
}
};

class BinaryExpression : public Expression
{
public:
std::string interpret(std::string line) override
{
int value = StringParser::getValue(line);
std::stringstream stream;
while(value)
{
stream << (value & 1);
value >>= 1;
}
return stream.str();
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class StringParser
{
StringParser() {}

static std::vector<int> kmpTable(const std::string &s)
{
int len = s.length(), j = 0;
std::vector<int> table(len, 0);

for(int i = 1; i < len; i++) {
while (j>0 && s[i] != s[j])
j = table[j-1];
if(s[i] == s[j])
table[i] = ++j;
}

return table;
}
public:
static std::optional<std::string> contains(const std::string &pattern, const std::string& context)
{
std::vector<int> table = kmpTable(pattern);
int patternLen = pattern.length(), contextLen = context.length(), j = 0;

for(int i = 0; i < contextLen; ++i){
while(j>0 && context[i] != pattern[j])
j = table[j - 1];

if(context[i] == pattern[j])
if(j == patternLen - 1)
return context.substr(i + 1);
else
j++;
}
return std::nullopt;
}

static int getValue(const std::string& context)
{
int ret = 0, len = context.length(), index = 0;
while(context[index] == ' ')
++index;

while(index < len && '0' <= context[index] && context[index] <= '9')
ret = (ret << 3) + (ret << 1) + (context[index++] & 0b1111);

return ret;
}
};

java 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Client {
public static void main(String[] args) {
Expression expression = new IntegerConverterExpression();
String line1 = "Binary 1234";
String line2 = "Hex 1234";
String line3 = "Binary 1234";
String line4 = "Hex1234fakjwefakljhfawekljh";
String line5 = "What did i say?";
String line6 = "Hex 987654321";

System.out.println(expression.interpret(line1));
System.out.println(expression.interpret(line2));
System.out.println(expression.interpret(line3));
System.out.println(expression.interpret(line4));
System.out.println(expression.interpret(line5));
System.out.println(expression.interpret(line6));
}
}
1
2
3
public interface Expression {
String interpret(String line);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class IntegerConverterExpression implements Expression{
private static final String UNSUPPORT = "Unsupported Expression : ";

private enum Support{
HEX("Hex"),
BINARY("Binary");

private String value;

private Support(String value) {
this.value = value;
}

public String getValue() {
return this.value;
}
}

private Map<Support, Expression> expressionMapper;

public IntegerConverterExpression() {
expressionMapper = new TreeMap<>();
expressionMapper.put(Support.HEX, new HexExpression());
expressionMapper.put(Support.BINARY, new BinaryExpression());
}

@Override
public String interpret(String line) {
Optional<String> result;

for(Map.Entry<Support, Expression> entry : expressionMapper.entrySet()) {
result = StringParser.contains(entry.getKey().getValue(), line);
if(result.isPresent())
return entry.getValue().interpret(result.get());
}

return UNSUPPORT + line;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class HexExpression implements Expression{
@Override
public String interpret(String line) {
int value = StringParser.getValue(line);
return Integer.toHexString(value);
}
}

public class BinaryExpression implements Expression{
public String interpret(String line) {
int value = StringParser.getValue(line);
return Integer.toBinaryString(value);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class StringParser {
private StringParser() {}

private static int[] kmpTable(String s)
{
int len = s.length(), j = 0;
int[] table = new int[len];

for(int i = 1; i < len; i++) {
while (j>0 && s.charAt(i) != s.charAt(j))
j = table[j-1];
if(s.charAt(i) == s.charAt(j))
table[i] = ++j;
}

return table;
}

public static Optional<String> contains(final String pattern, final String context)
{
int[] table = kmpTable(pattern);
int patternLen = pattern.length(), contextLen = context.length(), j = 0;

for(int i = 0; i < contextLen; ++i){
while(j>0 && context.charAt(i) != pattern.charAt(j))
j = table[j - 1];

if(context.charAt(i) == pattern.charAt(j))
if(j == patternLen - 1)
return Optional.of(context.substring(i + 1));
else
j++;
}
return Optional.empty();
}

public static int getValue(final String context)
{
int ret = 0, len = context.length(), index = 0;
while(context.charAt(index) == ' ')
++index;

while(index < len && '0' <= context.charAt(index) && context.charAt(index) <= '9')
ret = (ret << 3) + (ret << 1) + (context.charAt(index++) & 0b1111);

return ret;
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/23/Design%20Pattern/InterpreterPattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.