본문 바로가기

PROGRAMMING/Design Pattern

[GoF] 프로토타입 (Prototype) 패턴

프로토타입 (Prototype) 패턴 생성 패턴

원형이 되는 객체를 사용하여 생성할 객체의 종류를 정의하고, 이를 복제하여 새로운 객체를 생성합니다.


 

클라이언트는 간혹 객체를 독립적으로 복제해서 사용하고자 할 때가 있다. 이럴 경우, 단순히 객체를 새로 생성해야 할 뿐만 아니라, 객체 내부의 값을 모두 복사해야 할 것이다. 클라이언트가 직접 객체의 내부 값을 모조리 가져와서 새로 생성된 객체에 다시 넣어준다면 복제가 되었다고 할 수 있겠지만, 그것은 객체와의 의존이 생긴다는 의미를 가지기도 하고 객체가 복잡해진다면 감당하기 어려워질 수 있다.

그래서 원형 (Prototype) 이란 뜻을 가진 프로토타입 패턴은 객체를 복제할 수 있도록 해당 클래스 내에 복제에 대한 내용을 구현하여, 객체를 통해 복제된 객체를 반환 받을 수 있게 한다(보통 Clone() 함수를 통해 객체를 복제할 수 있도록 한다). 클라이언트는 복사할 객체를 참조하기만 하면 지정된 속성의 객체를 계속해서 복제하여 생성할 수 있다.

프로토타입 패턴을 이용하면 추상 팩토리 패턴과는 다르게 별도의 클래스를 만들거나 상속하지 않아도 복제 할 객체의 속성만 지정하면 참조를 통해 생성할 객체의 형식을 동적으로 규정할 수 있다. 그러나, 하위 클래스를 만들 때마다 복제 형식을 갱신해야 하므로 클래스 내의 코드가 길어질 수 있다.

프로토타입 패턴을 사용할 시 객체를 복사할때 객체 내의 참조값을 그대로 가져가 쓰는 얕은 복사(Swallow Copy)를 수행할 지, 아니면 객체 내의 참조 값을 새로 생성해 쓰는 깊은 복사(Deep Copy)를 수행할 지 유의해야 한다.

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
public class PatternPrototype
{
    public interface IPrototype<T> where T : IPrototype<T>
    {
        T Clone();
    }
 
    public interface IProduct
    {
        void Operation();
    }
 
    public class Product : IPrototype<Product>, IProduct
    {
        private string _name;
 
        public Product(string name)
        {
            _name = name;
        }
 
        public Product Clone()
        {
            return new Product(_name);
        }
 
        public void Operation()
        {
            Console.WriteLine("Operate " + _name + " (" + GetHashCode() + ")");
        }
    }
 
    public static void Main(string[] args)
    {
        Product prototype = new Product("ProductA");
 
        IProduct product = prototype;
        product.Operation();
 
        product = prototype.Clone();
        product.Operation();
    }
}
cs

1
2
3
Operate ProductA (46104728)
Operate ProductA (12289376)
Press any key to continue . . .
cs

위의 예제에서는 프로토타입 패턴에 대한 일반화된 인터페이스를 정의하여 사용하였다(IPrototype <T>). Product 클래스가 프로토타입 인터페이스를 상속하여 클라이언트에게 복제된 객체를 Clone() 함수를 통해 지정된 형식으로(Product 타입) 반환할 수 있게 한다. Clone() 에서 반환된 객체는 _name 필드가 복사되어 같은 객체로 보일 수도 있겠지만, 서로 해시(주소) 값이 다르므로 복제되어 새로 생성된 객체이다.