[Design pattern] 1-5. 프로토타입 패턴 (Prototype pattern)
안녕하세요. 명월입니다.
이 글은 디자인 패턴의 프로토타입 패턴(Prototype pattern)에 대한 글입니다.
프로토타입은 패턴 자체는 굉장히 단순한 패턴입니다만, 개념적으로 포인터와 스택, 힙 메모리에 대한 개념을 잘 모르신다면 이해하기 어려울 수도 있는 패턴입니다.
우리가 프로그램 상에서 클래스의 인스턴스를 생성하게 되면 변수에 포인터 주소가 입력되고 주소에 따른 인스턴스가 힙 메모리에 할당이 됩니다. 그래서 변수로 새로운 객체를 생성하지 않고 등호(equal: =) 기호로 새로운 변수명으로 인스턴스의 주소 값을 넘기면 두개의 변수에서 하나의 인스턴스를 가르키기 때문에 두 변수의 처리에 있어서 데이터 영향이 생깁니다.
using System;
namespace Example
{
// 예제 클래스
class Node
{
// 데이터 프로퍼티
public int Data { get; set; }
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// 인스턴스 생성
var node = new Node();
// node.Data의 프로퍼티에 1이란 값을 넣는다.
node.Data = 1;
// 새로운 변수를 생성하고 node의 인스턴스 주소를 넣는다.
var nodeClone = node;
// nodeClone.Data에 2의 값을 넣는다.
nodeClone.Data = 2;
// 이 때, node.Data의 값은 1일까 2일까?
Console.WriteLine(node.Data);
// 메모리 주소 출력
Console.WriteLine(node.GetHashCode());
Console.WriteLine(nodeClone.GetHashCode());
// 아무 키나 누르면 종료
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
그렇죠.. 당연히 영향이 가죠.. 결과를 보시면 node 변수와 nodeClone 변수의 메모리 주소가 같다는 걸 확인할 수 있습니다.
위의 그림같은 구조로 두개의 변수에 하나의 인스턴스가 묶여 있어서 그렇습니다.
link - [C#] 10. 인스턴스 생성(new)과 메모리 할당(Stack 메모리와 Heap 메모리) 그리고 null
link - [Java] 10. 메모리 할당(stack 메모리와 heap 메모리 그리고 new)과 Call by reference(포인터에 의한 참조)
그렇다면 반대로 저 인스턴스의 주소 값을 복사하지 말고 클래스 안의 데이터는 모두 같게 하고 클래스를 복제할 수 없을까?
위 예제 클래스는 워낙 단순한 구조라 새로 new Node하고 Data 값을 복사하는 것으로 끝나지만, 복잡한 클래스이고 대부분의 맴버 함수가 private으로 설정되어 있을 경우에는 단순하게 복사하는 게 안되겠네요.
출처 - https://en.wikipedia.org/wiki/Prototype_pattern
#pragma once
#include <stdio.h>
#include <iostream>
using namespace std;
// 예제 클래스
class Node {
private:
// 맴버 변수
int _data;
public:
// 생성자
Node(int data) {
// 맴버 변수에 데이터를 넣는다.
this->_data = data;
}
// 출력 함수
void print() {
// 콘솔 출력
cout << "data = " << this->_data << endl;
}
// 프로토타입(prototype)의 메모리 복사
Node* clone()
{
// 새로운 인스턴스 리턴
return new Node(*this);
}
};
// 실행 함수
int main() {
// 인스턴스 생성
Node* node = new Node(1);
// 인스턴스 복사
Node* nodeClone = node->clone();
// 출력 함수 호출
node->print();
nodeClone->print();
// 메모리 주소 출력
cout << node << endl;
cout << nodeClone << endl;
// 메모리 해제
delete node;
delete nodeClone;
return 0;
}
C/C++에서는 단순하게 clone 함수에서 새로운 인스턴스를 생성하는 new 키워드를 사용하고 this를 통해서 메모리를 넣으면 인스턴스가 복사가 됩니다.
결과를 보시면 data에 들어가 있는 값은 같지만 메모리 주소가 다른 것을 확인할 수 있습니다.
// 프로토타입을 사용하기 위해서는 Cloneable 인터페이스를 상속받아야 한다.
class Node implements Cloneable {
// 맴버 변수
private int data;
// 생성자
public Node(int data) {
// 맴버 변수에 값 설정
this.data = data;
}
// 출력 함수
public void print() {
// 콘솔 출력
System.out.println("data = " + data);
}
// 프로토타입(prototype)의 메모리 복사
public Node clone() {
try {
// 메모리 복사한다.
return (Node) super.clone();
} catch (CloneNotSupportedException e) {
// Cloneable 인터페이스를 상속 받지 않으면 에러가 발생한다.
return null;
}
}
}
public class Program {
// 실행 함수
public static void main(String[] args) {
// 인스턴스 생성
var node = new Node(1);
// 인스턴스 복사
var nodeClone = node.clone();
// 출력 함수 호출
node.print();
nodeClone.print();
// 메모리 주소 출력
System.out.println(node.hashCode());
System.out.println(nodeClone.hashCode());
}
}
자바의 경우는 Cloneable 인터페이스를 상속받아야 프로토타입 함수 clone 함수를 사용할 수 있습니다. 그리고 Object 클래스에서는 clone이 protected의 접근 제한자로 설정되어 있기 때문에 public으로 재정의해야 합니다.
자바도 맴버 변수의 값은 같은 것으로 확인이 됩니다만 메모리 주소는 다르니 서로 다른 인스턴스라는 것을 확인 할 수 있네요.
using System;
namespace Example
{
// 예제 클래스
class Node
{
// 맴버 변수
private int data;
// 생성자
public Node(int data)
{
// 맴버 변수에 값을 설정
this.data = data;
}
// 출력 함수
public void Print()
{
// 콘솔에 출력
Console.WriteLine("data = " + this.data);
}
// 프로토타입(prototype)의 메모리 복사
public Node Clone()
{
// Object 클래스에 protected 형태로 존재한다.
return this.MemberwiseClone() as Node;
}
}
class Program
{
// 실행 함수
static void Main(string[] args)
{
// 인스턴스 생성
var node = new Node(1);
// 인스턴스 복사
var nodeClone = node.Clone();
// 출력 함수 호출
node.Print();
nodeClone.Print();
// 메모리 주소 출력
Console.WriteLine(node.GetHashCode());
Console.WriteLine(nodeClone.GetHashCode());
// 아무 키나 누르면 종료
Console.WriteLine("Press any key...");
Console.ReadLine();
}
}
}
C#에서는 java와 다르게 따로 상속받아야 할 인터페이스는 없습니다. java와 마찬가지로 Object 클래스에 MemberwiseClone의 함수가 protected로 설정되어 있기 때문에 public으로 재정의해야 합니다.
역시나 맴버 변수의 값은 같은 것으로 확인이 되지만 메모리 주소가 다른 것을 확인할 수 있습니다.
여기까지 디자인 패턴의 프로토타입 패턴(Prototype pattern)에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.