1. 인터프리터 패턴
인터프리터 패턴은 간단한 언어의 문법 표현을 정의하고 해석하는 패턴입니다. 자주 사용하는 구문을 하나의 문법으로 정의하고 인터프리터를 통해 문장을 해석합니다. 보통 SQL을 파싱하거나, 기호를 처리하기 위해 사용하는 패턴입니다.
컴포지트 패턴과 유사합니다. Context 객체는 모든 Expression 객체에서 사용하는 공통된 정보를 가지고 있으며, Expression 객체는 표현할 문법을 나타냅니다. Terminal Expression 객체는 그 자체로 종료되는 Expression이며, Non Terminal Expression은 다른 Expression을 참조하고 있는 Expression 입니다.
예제코드를 작성해보겠습니다. 후위 표기법으로 표기된 문장을 계산하는 시나리오 입니다.
PostfixExpression.java: 후위표기법의 인터페이스 입니다. 문법 객체가 interpret 메소드를 통해 연산을 수행하도록 정의합니다.
PlusExpression.java:
MinusExpression.java: MultiplyExpression.java:
DivisionExpression.java:
문법을 표현할 객체들을 만들어줍니다. 각 객체들은 PostfixExpression 인터페이스를 상속받아서 interpret 메소드를 구현합니다. 위에 객체들은 모두 NonTerminalExpression 객체이기 때문에 재귀호출을 통해 왼쪽과 오른쪽 객체의 최종값을 연산합니다.
VariableExpression.java: TerminalExpression 객체입니다. 연산없이 최종값을 출력합니다.
PostfixParser.java: 파서 객체입니다. 후위 표기법의 연산은 스택을 사용하므로 스택을 통해 context를 각각 연산합니다.
Client.java: 클라이언트 객체입니다. 파서를 통해 연산 객체를 생성해주고 interpret 메소드로 연산을 합니다.
"123*+4-"는 1 + (2*3) - 4 이므로 3이 출력됩니다.
인터프리터 패턴은 자주 등장하는 문제의 패턴을 언어와 문법으로 정의할 수 있고 기존 코드를 변경하지 않고 새로운 expression을 만들 수 있다는 장점이 있는 반면에 복잡한 문법을 표현하려면 expression과 parser가 복잡해지는 단점이 있습니다.
자바에서는 정규표현식과 스프링의 expression language에 해당 패턴을 사용합니다.
2. 이터레이터 패턴(Iterator Pattern)
이터레이터 패턴은 내부 컬렉션(Collection)의 구현 방법을 노출시키지 않고 복합 객체의 원소를 순차접으로 접근할 수 있는 방법을 제공하는 패턴입니다. 쉽게 말해서 리스트 내부 원소들을 순차적으로 표현하는 패턴입니다.
보통 컬렉션을 순차적으로 접근하는데 for문을 많이 사용합니다. 그러나 컬렉션 객체는 종류마다 원소의 관리 방법에 조금씩 차이가 있습니다. 다시 말해서 원소 객체에 접근하기 위해서는 컬렉션 객체에 대한 정보를 미리 알고 있어야 합니다. 즉, 컬렉션 객체에 대한 의존성이 강합니다. 이터레이터 패턴은 원소 객체와 컬렉션 객체를 분리하여 이러한 의존성을 분리해줍니다.
Iterator는 원소를 순회하는데 필요한 인터페이스를 제공하며, ConcreteIterator는 Iterator의 인터페이스를 구현한 클래스 입니다. 원소의 현재 위치를 기억합니다.
Aggregate는 Iterator 객체를 생성하는 인터페이스이며, ConcreteAggregate는 Aggregate의 인터페이스를 구현한 클래스입니다. ConcreteIterator의 인스턴스를 반환합니다.
예제 코드를 작성해보겠습니다. 5명의 학생이 있고, 5명의 학생을 하나의 반(class)으로 묶었을 때 학생들의 이름을 순차적으로 출력하는 시나리오 입니다.
Student.java: 학생 클래스입니다.
Aggregate.java: 컬렉션의 인터페이스 입니다.
Class.java: 컬렉션 인터페이스를 구현한 학반(Class) 클래스 입니다. 학생(Student)들의 배열을 멤버 변수로 가집니다.
Iterator.java: 이터레이터 인터페이스 입니다.
ClassIterator.java: 이터레이터 인터페이스를 구현한 학반(Class)의 이터레이터 입니다.
Client.java: 학반(Class)에 학생(Student)들을 넣고 이터레이터 객체를 생성합니다. 이로써 학반(Class)의 메소드를 모르더라도 이터레이터 객체를 통해 순회가 가능한 구조가 만들어 졌습니다!
만약 이터레이터 패턴을 사용하지 않았더라면 아래와 같이 for문을 통하여 학생에 접근했어야 합니다.
보는 바와 같이 Class 객체의 getStudentAt 메소드를 사용합니다. 이는 Class 객체에 대한 의존도가 높아져 Class 클래스 코드에 변화가 있다면 Client 클래스의 코드도 변경되어야 함을 의미합니다. 이것은 개방폐쇄원칙(OCP)에 부합하지 않습니다.
이터레이터 패턴을 사용하면 Class 클래스 코드에 변화가 있어도 이터레이터 클래스가 올바르게 작성된다면 Client 클래스 코드를 변경할 필요가 없어집니다. 즉, 개방폐쇄원칙(OCP)에 부합하게 됩니다. |