작성: 한동훈(traxacun at unitel.co.kr)
작성일: 2003. 2. 24
Prototype 패턴은 인스턴스를 생성하는 데 따른 부하가 복제하는 데 드는 부하보다 큰 경우에 유용하게 사용된다. Prototype 패턴이 유용한 경우는 다음과 같다. 대규모 데이터베이스에 다수의 질의를 통해서 레코드를 가져오고 데이터를 처리해야하는 경우를 생각해보자. 이 경우에 처음에 가져온 데이터셋을 조작하여 별도의 질의를 만들어내지 않고 다른 종류의 결과를 생성해낼 수 있을 것이다.(ADO.NET에 익숙하다면 단절된 레코드셋(Disconnected DataSet)과 데이터셋 필터링을 생각해보자.)
그림1. Prototype
GoF의 책과는 다이어그램이 다소 다르지만 의도는 동일하다. Prototype의 종류에 따라 적절한 ConcretePrototype을 선택하고 ConcretePrototype.Clone()을 통해서 복사본을 생성한다. 주의할 것은 여기서 생성하는 복사본은 단순 복사본(Shallow Copy)이라는 것이다.
Prototype 패턴에 대한 코드를 살펴본 다음에 간단히 단순 복사본(Shallow Copy)와 전체 복사본(Deep Copy)에 대해서 살펴보자.
그림2. Prototype 클래스
그림3. ConcretePrototype1 클래스
ConcretePrototype 클래스에서 복사본을 생성할 때 단순 복사본(Shallow Copy)을 생성한다는 것을 알 수 있다. 닷넷에서는 단순 복사본을 생성하는 메서드 MemberwiseClone()이 제공된다. MemberwiseClone() 메서드는 Object 클래스의 멤버이며 재정의(override)될 수 없다. MemberwiseClone() 메서드는 단순히 Object 클래스의 복사본을 반환하기 때문에 적절한 형식으로 캐스팅하여야한다. 따라서 위 코드에서는 Prototype 클래스로 캐스팅한다. 만약 여러분 고유의 복사본을 생성하고 싶다면 IConeable 인터페이스를 구현해야한다. 이 인터페이스는 Clone() 메서드만 정의한다. IConeable 인터페이스를 구현하는 경우는 전체 복사본(Deep Copy)을 구현할 때 뿐이라는 것도 알아두기 바란다.
그림4. ConcretePrototype2 클래스
그림5. Tester 클래스
Prototype 패턴에서 자주 인용되는 예로는 색상 시스템이 있다. 색상에 대한 원형은 ColorPrototype(Prototype)으로 정의할 수 있으며, 실제 색상은 Color(ConcretePrototype)로 정의할 수 있다. 마찬가지로 이러한 색상을 관리하는 색상 관리자 ColorManager(Client)로 정의된다. 대규모 데이터베이스에 다수의 질의를 보내야하는 경우 하나의 레코드셋의 복제본을 생성하여 처리하는 것이 유리한 것처럼 색상에 대한 정보 역시 대부분의 정보가 공유되고 일부의 정보만이 다르다는 것을 알 수 있다. 이런 경우에도 Prototype 시스템은 유용하게 쓰인다. 이러한 관점에서 필요한 경우에 숫자를 나타낼 때만 문자열을 파싱하는 Examplar 패턴과도 비슷하다는 것을 알 수 있다. 즉, 전체 인스턴스를 새로 생성하지 않고 기존 데이터를 이용하여 다른 결과를 생성해내기 위해 쓰이기 때문에 Prototype 패턴은 단순 복사본(Shallow Copy)을 사용한다.
단순 복사본(Shallow Copy) vs 전체 복사본(Deep Copy)
단순 복사본과 전체 복사본에 대해서는 이미 많은 이야기가 있었기 때문에 여기에 옮겨두는 것은 단순히 기존의 것을 복사(Copy)하는 것에 지나지 않는다는 것을 기억해주기 바란다.
객체 X가 객체 A, B를 참조하고, 객체 A는 객체 M을 참조한다고 할 때 X에 대한 단순 복사본 Y는 객체 A와 B를 참조한다. 객체 X에 대한 전체 복사본 Z가 있다면 객체 Z는 객체 C와 D를 직접 참조한다. 객체 C와 D는 객체 A와 B의 복사본이다. 마찬가지로 객체 C는 객체 N를 직접 참조하며, 객체 N은 객체 M의 복사본이다.
단순 복사본은 참조하는 객체까지 복사하지 않지만 전체 복사본은 참조하는 객체까지 복사한다. 즉, 단순 복사본은 원본의 참조를 유지하지만 전체 복사본은 원본의 참조를 유지하지 않고 참조되는 객체의 새로운 복사본을 참조한다.
단순 복사본은 MemberwiseClone() 메서드를 호출해서 얻을 수 있으며 이렇게 얻은 복사본은 원본과 같은 참조를 가진다. MeberwiseClone()은 Object 클래스의 멤버이며, 모든 닷넷 클래스는 Object 클래스를 상속하기 때문에 모든 클래스에서 MemberwiseClone() 메서드를 호출하여 단순 복사본을 얻을 수 있다. 만약 새로운 전체 복사본을 생성하길 원한다면 IConeable 인터페이스를 구현해야하며, 이 인터페이스는 Clone() 메서드만 정의하고 있다. 즉, Clone() 메서드만 구현하면 된다.
만약 상속 클래스에서 완전한 복사본을 생성할 필요가 있다면 전체 복사본(Deep Copy)을 사용해야한다. 잘 알겠지만 Child 클래스의 인스턴스가 생성될때 내부적으로는 Parent 클래스의 인스턴스가 동시에 생성되고 유지된다. 단순 복사본은 두 개의 Child 클래스를 생성하더라도 하나의 참조 즉, Parent 클래스를 가리키게 된다. 여러분의 의도가 Parent 클래스 역시 독자적인 상태를 유지하고, Child 간에 상태를 공유하는 것을 원하지 않는다면 IConeable 인터페이스를 구현해야한다.
마치며
이로서 대표적인 생성 패턴들을 살펴보았다. 각각의 생성 패턴들은 나름대로의 필요성을 갖고 있다.
Singleton이나 Monostate가 생성되는 인스턴스의 생성과 관련된 것이라면 Abstract Factory나 Builder 패턴은 복잡한 시스템을 구성하는 관련된 클래스들의 인스턴스를 어떻게 하면 보다 효율적으로 생성하면서 유지보수하기 쉬운 구조를 유지할 것인가에 무게를 두고 있다고 할 수 있다. Factory Method는 전체를 구성하는 각 부품들의 단계별 생성에 중심을 두고 있다. 그로인해 다양한 조합을 가지는 전체를 구성할 필요가 있는 경우에 유용하게 사용된다. Prototype 패턴은 많은 인스턴스들이 대부분 같은 정보를 유지하고, 일부만 다른 정보를 가지는 경우에 인스턴스 생성에 따른 부하를 줄이고, 복사본(특히, Shallow Copy)을 이용하여 객체 생성을 줄이고 자원을 보다 효율적으로 사용하는 데 중점을 둔다고 할 수 있다. Prototype 패턴에 관심이 있는 독자라면 이와 같은 문제에 대한 또 다른 해결책인 Type Object 패턴에도 관심을 가져보기 바란다.
Monostate와 Utility 패턴에 대해서도 살펴보았으나 Monostate Utility 패턴에 대해서는 소개하지 못한 아쉬움이 있다. Monostate Utility 패턴은 닷넷 리모팅 클래스에서 사용되고 있다.
최신 콘텐츠