오르카의 아틀리에

<Java 언어로 배우는 디자인 패턴 입문>이라는 책을 가지고 복습하는 것이기 때문에 책에 나와 있는 패턴을 순서대로 공부해보려고 한다. 좀 멀리 떨어져 있더라도 비슷한 모양이나 역할의 패턴들이 있기는 하지만, 글 내부에 관련 패턴 형식으로 정리하면 될 것 같다.


다른 학교는 처음에 어떤 언어를 배울지는 모르겠다. 내가 알기로는 H 대학교는 파이썬을 배우고, K 대학교에서는 C를 먼저 배운다고 알고 있다. 하지만 인하대학교에서는 OOP라는 명목으로 C++를 가르치고 있고 다른 학교도 OOP라는 이름으로 Java를 가르칠 것이라고 생각한다. 그 두 개를 배워서 써본 사람이라면 를 어디서 쓰지는 않아도 들어봤을 것으로 생각한다.


iterator?! C++의 그것?

맞다. 여러분이 C++ 에서 사용하는 iterator다. 반복 자라 고도하는데 다음 코드를 보면 iterator를 어떨 때 사용하는지 감이 잡힐 것으로 생각한다.



대략 이렇게 사용하는 STL이었다. 근데 이렇게 반복할거면 차라리 우리가 흔히 알고있는 int i를 이용하여 인덱스에 직접 접근하면 되지 않을까? 아무리 봐도 기존 방법보다 느려 보인다. 왜 무거워 보이는 iterator를 만들고 활용하는 걸까? 만약 이런 고민이 들었다면 아주 바람직하다고 할 수 있다. 확실히 iterator를 사용하면 index를 사용하는 것보다 귀찮고, 느리다.


iterator 패턴의 의도?!

그런데도 왜 사용하는 걸까? 답은 바로 "캡슐화"에 있다. iterator를 사용하는 가장 큰 이유는 구현과 분리해서 반복할 수 있다는 점이다. 그런데 여기서 또 의문을 가질 수 있을 것이다.


"분리해서하면 뭐가 좋은데?"


사실 우리 학교는 OOP 시간에 캡슐화나 정보 은닉보다 추상화나 다형성에 초점을 맞추어 수업하기도 했고, 일반적인 과제로는 캡슐화의 중요성을 느끼기엔 부족했던 점도 있다. 때문인지 주위 학교 동기들의 코드들을 보면 캡슐화와 은닉화를 지키지 못한 코드들을 많이 볼 수 있다. 예를 들어 getter/setter의 무분별한 사용이라든지(이러면 public이랑 다를 바가 없음….), 여러 객체 사이의 메소드들이 의존성이 높게 만들어져있는다든지... 다음 상황을 한번 살펴보면 캡슐화가 중요한 이유를 조금이나마 이해할 수 있다.



좀 더 와 닿는 설명을 위해 일부러 어글리하게 코드를 작성했다. 간단하게 보면 ShoeCloset 이라는 신발장 안에 Shoes 객체들이 배열로 저장되어있는 상황이고 main에서 신발장에 있는 모든 신발의 이름을 출력하는 코드이다. for 문을 보면 알겠지만, 내부에서 ShoeCloset에서 구현된 메소드에 의존적이다.


그런데 만약에 ShoeCloset 에서 정적 배열을 사용하고 있는 것이 마음에 들지 않아서 STL Vector로 바꾸는 상황을 상상해보자, 동적으로 신발장을 넣고 빼고 하기 위해 shoesBoxs를 vector로 바꾸기만 하더라도, for문 내부를 바꿔야 하는 상황이 발생한다.



나는 단지 ShoeCloset 의 일부분만 변경했을 뿐인데 바꾸어야 하는 코드들이 이곳저곳에서 생기는 것이다. 이게 만약 좀 더 큰 프로젝트, 좀 더 많은 메소드를 통해 객체와 메소드들이 좀 더 의존적인 프로젝트였다고 생각해보자. 헬게이트가 열리는 것이다. 이럴 때 소스를 집어 던지고 싶어진다. 반면, iterator를 활용하면 for 문안에서 ShoeCloest의 메소드를 직접 가져다 사용하지 않아도 된다.


"분리를 잘해놓으면 코드를 수정할 때

수정해야 하는 부분이 명확해지고, 간결해진다."

iterator UML

캡슐화에 관한 이야기가 길어졌는데, 이쯤이면 iterator의 의도와 캡슐화의 중요성을 느꼈을 것으로 생각한다. 이제 UML을 살펴보자.



UML을 살펴보면 Aggregate가 Iterator를 만들고 있고, ConcreteAggregate와 ConcreteIterator가 각각 둘을 상속받고 있는 관계이며 ConcreteIterator가 ConcreteAggregate를 소유하고 있다. 간단하게 역할을 정리해보면 Aggregate는 "자신을 순차적으로 탐색해줄 객체"를 만드는 역할을 하고, 그 역할을 상속받는 ConcreteAggregate가 구현하여 ConcreteIterator를 만들게된다. 또한, ConcreteIterator는 ConcreteAggregate를 탐색해야하므로 소유하고 있는 것이다.


Sample code

역시 프로그래머라면 코드를 보고 이야기할 줄 알아야한다. 위의 내용들을 다시 이해해보고 예제 코드를 한번 보도록하자.



완벽한 예제는 아니다. Aggregate와 Iterator를 만들어 상속시키지 않았고 본드칠을 몇 번 한 예제지만 이해하기는 무리 없을 것으로 생각한다. C++에서는 이렇게 직접 구현하지 않고 STL을 사용하면 된다. 템플릿을 이용해서 잘 구현해놓았기 때문에 특별한 경우가 아니면 직접 만들 필요는 없다.


정리

01. 패턴의 의도 : 캡슐화

02. 패턴의 동작 : 자신을 순차적으로 탐색해줄 객체를 제작하고 그 객체가 탐색하는 역할을 수행