안녕하세요. 명월입니다.
이 글은 디자인 패턴의 프로토타입 패턴(Prototype pattern)에 대한 글입니다.
프로토타입은 패턴 자체는 굉장히 단순한 패턴입니다만, 개념적으로 포인터와 스택, 힙 메모리에 대한 개념을 잘 모르신다면 이해하기 어려울 수도 있는 패턴입니다.
우리가 프로그램 상에서 클래스의 인스턴스를 생성하게 되면 변수에 포인터 주소가 입력되고 주소에 따른 인스턴스가 힙 메모리에 할당이 됩니다. 그래서 변수로 새로운 객체를 생성하지 않고 등호(equal: =) 기호로 새로운 변수명으로 인스턴스의 주소 값을 넘기면 두개의 변수에서 하나의 인스턴스를 가르키기 때문에 두 변수의 처리에 있어서 데이터 영향이 생깁니다.
Copy! [소스 보기] Program.csusing 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
Copy! [소스 보기] C/C++ 프로토타입 패턴 예제#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에 들어가 있는 값은 같지만 메모리 주소가 다른 것을 확인할 수 있습니다.
Copy! [소스 보기] Java 프로토타입 패턴 예제// 프로토타입을 사용하기 위해서는 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으로 재정의해야 합니다.
자바도 맴버 변수의 값은 같은 것으로 확인이 됩니다만 메모리 주소는 다르니 서로 다른 인스턴스라는 것을 확인 할 수 있네요.
Copy! [소스 보기] C# 프로토타입 패턴 예제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)에 대한 글이었습니다.
궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.
'Study > Design Pattern' 카테고리의 다른 글
[Design pattern] 2-4. 데코레이터 패턴 (Decorator pattern) (0) | 2021.10.28 |
---|---|
[Design pattern] 2-3. 브릿지 패턴(Bridge pattern) (0) | 2021.10.27 |
[Design pattern] 2-2. 컴포지트 패턴(Composite pattern) (0) | 2021.10.27 |
[Design pattern] 2-1. 어댑터 패턴(Adapter pattern) (0) | 2021.10.26 |
[Design Pattern] 1-4. 추상 팩토리 패턴 (Abstract factory pattern) (0) | 2021.10.15 |
[Design Pattern] 1-3. 팩토리 메서드 패턴 (Factory method pattern) (0) | 2021.06.23 |
[Design Pattern] 1-2. 빌더 패턴(Builder pattern) (0) | 2021.06.11 |
[Design Pattern] 1-1. 싱글톤 패턴 (Singleton pattern) (0) | 2021.06.09 |