참고 : JAVA 언어로 배우는 디자인 패턴 입문
안녕하세요. 물류플랫폼 트레드링스 개발팀 양갱 입니다.
간만에 복습! Q.디자인 패턴을 쓰는 이유는 뭐죠?
맞아요. 반복적인 코드는 줄이고 효율적으로 개발하기 위해서 입니다.
자, 그럼 오늘 공부할 디자인 패턴은 바로 Composite 패턴 입니다.
아C 영어 울렁증... 뭐지...
울지말고 같이 Composite의 뜻을 찾아보아요.
Composite를 검색해 보니깐 합성의,, 합성물.. 이런 느낌의 단어인거 같네요.
맞아요. 오늘 공부할 Composite 패턴도 어떤 혼합물을 만드는 패턴입니다.
예를들어, 컴퓨터에 있는 파일(=디렉터리) 다들 아시죠?
컴퓨터 폴더 안에는 파일이 있거나 또 다른 폴더가 있기도 합니다. (저의 직박구리 폴더가 공개됬네요.. 흠흠..)
또 그 하위 디렉터리 안에는 다른 파일이나 하위 폴더가 있기도 합니다.
컴퓨터의 폴더는 이와 같이 '상자 안의 상자' 로 된 구조, 즉 재귀적인 구조를 만들어 냅니다.
폴더와 파일은 서로 다르지만 모두 '폴더 안에 넣을 수 있는 것' 입니다.
디렉터리와 파일을 합해서 '디렉터리 엔트리'라고 부르기도 합니다.
디렉터리 엔트리라는 이름으로 디렉터리와 파일을 같은 종류로 동일시하고 있습니다.
이번에 공부할 Composite 패턴도 무언가를 담는 그릇과 내용물을 동일시해서 재귀적인 구조를 만들기 위한 디자인 패턴 입니다.
자, 그럼 '상자안의 상자' 속으로 떠나 볼까요~ GOGO씽!
Composite 패턴의 예제는 파일과 폴더를 도식적으로 표현하는 프로그램 입니다.
파일을 나타내는 클래스가 File 클래스이고 폴더를 나타내는 클래스가 Directory 클래스 입니다.
두가지를 하나로 모은 형태의 상위 클래스가 Entry 클래스입니다.
Entry 클래스는 디렉터리 엔트리를 나타내는 클래스로서 File과 Directory를 동일시하는 클래스 입니다.
Entry 클래스는 추상 클래스로서 디렉터리 엔트리를 표현하며 하위 클래스인 File 클래스와 Directory 클래스를 만듭니다.
디렉터리 안에 파일이나 폴더 즉, 디렉터리 엔트리를 넣는 메소드는 add() 입니다.
그러나 add를 구현하는 것은 디렉터리를 나타내는 하위 클래스인 Directory 클래스 입니다.
printList 메소드는 ‘종류’를 표시하는 메소드입니다.
인수없는 printList()와 인수있는 printList(String) 두 가지 메소드가 있습니다.
상위 클래스인 Entry를 상속해서 하위에서 구현하고 있습니다.
여기에서 "/" + this 라는 코드를 하고 있지만 문자열과 오브젝트를 더하면
자동으로 그 오브젝트의 toString() 메소드가 호출됩니다.
따라서 아까 상위 클래스의 toString()가 호출되고
내부에서 다시 하위에서 구현한 getName()와 getSize()를 호출하는 구조가 됩니다.
여기에는 Templete Method 패턴이 숨어있네요. (나중에 다시 다룰 예정입니다. 그냥 그런게 있다는 것만 알고 넘어가세유~~)
Directory 클래스는 디렉터리를 표현하는 클래스이며 Entry 클래스의 하위 클래스로 정의되어 있습니다.
변수 size에 entry의 크기를 더하고 있지만, 이 entry는 File의 인스턴스인지 Directory의 인스턴스인지 모릅니다.
두 경우 모두 동일한 메소드 getSize()로 크기를 얻을 수 있습니다.
이것이 바로 Composite 패턴의 특징인 '그릇과 내용물의 동일시'를 나타냅니다.
File의 인스턴스든, Directory의 인스턴스든 entry는 어쨌든 Entry의 하위 클래스의 인스턴스이기 때문에 getSize() 를 호출할 수 있는 것입니다.
만약, Entry의 하위 클래스로서 다른 클래스가 만들어져도, getSize()메소드를 구현할 것이기 때문에 Directory 클래스의 이부분을 수정할 필요가 없습니다.
바로 이겁니다! 이게 핵심이에요!!!
entry가 Directory의 인스턴스인 경우 entry.getSize()를 보면 Directory 안의 엔트리의 크기를 하나하나 더합니다.
또 그 안에 디렉터리가 있다면 다시 하위 디렉터리의 getSize()를 호출하고... 이렇게 재귀적으로 getSize()가 호출되는 것입니다.
여러분..펔펔.. 진정하시고.. '상자속의 상자'를 기억해 내세요.
add() 메소드도 마찬가지로 인수로 주어진 entry가 실제로 Directory 클래스의 인스턴스인지, File 클래스의 인스턴스인지 조사할 필요없이 directory 필드에 추가됩니다.
파일에 대해서 add()메소드를 잘못 호출했을 때 던져지는 예외입니다.
Main 클래스는 다음과 같은 디렉터리 구조를 만듭니다.
실행결과:
한가지 더! Composite 패턴은 그릇과 내용물을 동일시 하는 패턴이지만,
복수와 단수 역시 동일시 한다고 할 수 있습니다. 즉, 여러 개를 모아서 마치 하나인 것처럼 취급하는 것입니다.
예를들어, TEST1에서 키보드로 입력하고 TEST2에서는 파일에서 읽어서 입력하고 TEST3에서는 네트워크에서 입력한다고 하면
TEST1, TEST2, TEST3 세 개를 합해서 하나의 '입력 테스트'로 하고 싶을때 Composite 패턴을 사용할 수 있습니다.
지금까지 '상자속의 상자' Composite 패턴 이였습니다.