[Design Pattern] 프로토 타입 패턴

Prototype Pattern

Prototypical 인스턴스를 사용하여 생성할 객체의 종류를 명시하고 이렇게 만든 견본을 복사해 새로운 객체를 생성하는 패턴이다. 다수의 동일한 객체 생성시에 발생되는 객체 생성 비용을 효과적으로 줄일 수 있다. 하지만 순환 참조가 생기는 경우 패턴을 생성하기 힘들어진다.

활용성

  1. 인스턴스화할 클래스를 동적 로딩과 같은 런타임에 지정할 때
  2. 제품 클래스 계통과 병렬적으로 만드는 팩토리 클래스를 피하고 싶을 때
  3. 클래스의 인스턴스들이 서로 다른 상태 조합 중에 어느 하나일 때

프로토 타입 패턴 사용에 따른 결과

런타임에 새로운 제품 추가 및 삭제

프로토 타입 패턴으로 생성되는 인스턴스를 등록하는 것 만드로도 시스템에 새로운 제품을 쉽게 추가할 수 있다.

값들을 다양화함으로써 새로운 객체 명세

새로운 클래스를 손쉽게 객체 합성을 통해 새로운 행동을 정의할 수 있다. 즉, 객체 변수가 다른 클래스에 대한 참조를 가지고 있다면 이 참조자가 합성한 새로운 클래스만 정의하고 그 클래스의 인스턴스에 대한 참조자만 넘겨주면 새로운 행동이 정의되는 것 처럼 보인다.

구조를 다양화함으로써 새로운 객체 명세

구성요소와 부분 구성요소의 복합을 통해 객체를 구축하는 도중에 있어 편의를 위해 복잡한 사용자 정의 구조를 사용자가 인스턴스화 하여 그 상황에 맞는 구성요소를 정의할 수 있다.

서브클래스의 수를 줄임

프로토 타입 패턴은 새로운 객체를 만드는 것이 아닌 clone하기 때문에 팩토리 메소드 패턴의 Creator 클래스에 따른 새로운 상속 계층이 불필요하다.

동적 클래스에 따라 프로그램 설정

런타임 환경에서 정적으로 구성된 프로토 타입 객체를 동적으로 인스턴스로 사용할 수 있도록 구성해준다. 다음 코드를 보면 쉽게 이해가 간다.

1
2
3
4
5
6
7
8
9
void getConcretePrototype()
{
ConcretePrototype c("Coke", 100);
Prototype* prototypeClone = c.clone();
std::cout<<"MEMORY OF STATIC : "<<(void*)&c<<std::endl;
c.getInfo();
std::cout<<"MEMORY OF CLONE : "<<(void*)&prototypeClone<<std::endl;
prototypeClone->getInfo();
}
1
2
3
4
5
6
7
8
9
Concrete Prototype Copy Constructor Called
MEMORY OF STATIC : 0x7ffee9a957f0
NAME : Coke
MEMORY OF NAME : 0x7ffee9a957f8
SIZE : 100
MEMORY OF CLONE : 0x7ffee9a957e8
NAME : Coke
MEMORY OF NAME : 0x7f9bc6402b88
SIZE : 100

팩토리 메소드 패턴과의 차이

클래스 다이어그램을 보면 구조적으로도 차이가 있다. 팩토리 메소드는 Creator 클래스가 존재하고 이를 상속받는 ConcreteCreator가 존재하는 반면 프로토 타입 패턴에는 이 레이어가 생략된다. 또한 미리 만들어둔 객체를 통해 다양하게 일반화된 구성을 가진 identity가 다른 객체를 손쉽게 얻을 수 있다.

프로토 타입

구조

프로토 타입의 구조는 다음과 같다.

Prototype Pattern Diagram

C++ 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void PrototypePattern()
{
getConcretePrototype();
}

void getConcretePrototype()
{
Prototype* prototypeOrigin = new ConcretePrototype("Coke", 100);

Prototype* prototypeClone = prototypeOrigin->clone();

std::cout<<"MEMORY OF ORIGIN : "<<(void*)&prototypeOrigin<<std::endl;
prototypeOrigin->getInfo();

std::cout<<"MEMORY OF CLONE : "<<(void*)&prototypeClone<<std::endl;
prototypeClone->getInfo();
}
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
class Prototype
{
public:
virtual Prototype* clone() = 0;
virtual void getInfo() = 0;
};

class ConcretePrototype : public Prototype
{
char* name;
int nameLen;
int size;
public:
ConcretePrototype(const char* name, int size)
{
this->nameLen = strlen(name);
this->name = new char(this->nameLen + 1);
strcpy(this->name, name);
this->size = size;
}

ConcretePrototype(const ConcretePrototype& concretePrototype)
{
std::cout<<"Concrete Prototype Copy Constructor Called"<<std::endl;
this->nameLen = concretePrototype.nameLen;
this->name = new char(this->nameLen + 1);
strcpy(this->name, concretePrototype.name);
this->size = concretePrototype.size;
}

Prototype* clone() override
{
return new ConcretePrototype(*this);
}

void getInfo()
{
std::cout<<"NAME : "<<this->name<<std::endl;
std::cout<<"MEMORY OF NAME : "<<(void*)&name<<std::endl;
std::cout<<"SIZE : "<<this->size<<std::endl;
}
};

java 구현

1
2
3
4
5
6
7
8
public class Client {
public static void main(String[] args) {
Product product = new ConcreteProduct("Coke", 100);
Product clonedProduct = product.getClone();
product.getInfo();
clonedProduct.getInfo();
}
}
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
public interface Product extends Cloneable{
void getInfo();
Product getClone();
}

public class ConcreteProduct implements Product{
private String name;
private int size;

public ConcreteProduct(String name, int size) {
this.name = name;
this.size = size;
}

@Override
public void getInfo() {
System.out.println("Name : " + this.name);
System.out.println("Size : " + this.size);
}

@Override
public Product getClone() {
Product product = null;
try {
product = (Product)clone();
} catch (CloneNotSupportedException e) {
System.err.println(e.getMessage());
}
return product;
}

@Override
protected Object clone() throws CloneNotSupportedException {
ConcreteProduct cloneObj = (ConcreteProduct)super.clone();
//cloned object의 identify를 쉽게 확인하기 위해 Cloned prefix를 붙혔다
cloneObj.name = "Cloned " + name;
cloneObj.size = size;
return cloneObj;
}
}
Author: Song Hayoung
Link: https://songhayoung.github.io/2020/08/13/Design%20Pattern/PrototypePattern/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.