본문 바로가기

Go

golang 프로토타입패턴(Prototype Pattern with golang)

목적

프로토타입 패턴은 이미 존재하는 객체를 해당 클래스에 의존하지 않고 복사하는 패턴이다.

실세계 유추

실제 세계에서 프로토타입(시제품)이란 용어는 제품의 대량생산을 시작하기 전에 다양한 테스트를 수행하는데 사용된다
하지만 프로토타입은 실제 제품에 참여하지 않는 수동적인 역할을 한다

공업적 프로토타입이 실제로 스스로 복사하는것이 아니기 때문에, 패턴에 더 근접한 유추는 생물학의 세포 유사 분열 과정으로 볼수있다
분열 이후에 개별 세포 쌍이 형성된다. 원본 세포는 프로토타입처럼 행동하고 복사본을 만드는데 능동적인 역할을 가져간다.

문제

가지고 있는 객체의 복사본을 가지고 싶다면, 같은 클래스의 새로운 객체를 만들고 가지고 있는 객체의 값들과 모든 영역들을 복사해야 한다. 하지만 어떤 객체들은 외부로부터 보여지지 않는 private 영역을 가지고 있을 수 도 있고 복사하고자하는 객체의 클래스를 알아야 하기 때문에 코드는 해당 클래스에 의존성을 가지게 된다. 의존성에 신경을 쓰지 않으면 메소드의 매개 변수가 일부 인터페이스를 따르는 개체를 수락할 때 개체가 따르는 인터페이스만 알고 구상 클래스는 알 수 없어지는 문제가 발생한다.

해결

프로토타입 패턴은 복사하는 과정을 복사되는 객체에게 위임한다
모든 객체에게 코드와 객체의 클래스의 결합없이 객체를 복사할 수 있도록하는 공용 인터페이스를 선언한다. 보통 인터페이스는 clone 메소드 하나만을 포함한다. clone 메소드의 구현은 다른 클래스와 유사하다. 해당 메소드는 현재 클래스의 객체를 만들고 오래된 객체에서 새로운 객체로 모든 영역의 값들을 가져온다. 대부분의 프로그래밍 언어가 객체에게 같은 클래스에 속한 다른 객체의 private 영역에 접근할 수 있게 하기 때문에 private 영역 역시 복사할 수 있다. 복제를 지원하는 객체는 프로토타입이라고 불린다. 객체가 수십개의 영역을 가지고 수백개의 구성이 가능해지면 해당 필드를 복제하는것이 서브클래싱의 대안으로 작용할 수 있다.

다양한 방식들로 구성된 객체의 집합을 생성한다. 구성한 객체와 같은 객체를 필요로 한다면 새 객체를 긁어서 가져오는것 대신에 프로토타입을 복제하면 된다.

적용

  • 복사해야하는 객체의 구상 클래스에 의존하고 싶지 않을 때 프로토타입 패턴을 사용

서드파티의 객체들과 인터페이스를 통해 작업할 때 이런 경우가 발생한다. 해당 객체들의 구상 클래스들은 알려지지 않았고, 해당 클래스에 의존하고 싶지 않을때 프로토타입 패턴은 복제를 지원하는 모든 객체로 작업 할 수 있는 일반 인터페이스를 클라이언트 코드에게 제공한다. 이 인터페이스는 클라이언트 코드를 복제하는 객체의 구상 클래스로부터 독립시키게 된다.

  • 각각의 객체가 초기화하는 방법만 다른 서브클래스들의 수를 줄이고 싶을 때 프로토타입 패턴 사용

까다로운 구성을 요구하는 복잡한 클래스가 있다고 가정해보면 이 클래스를 구성하는 몇 가지 일반적인 방법이 있고, 코드는 앱을 통해 분산되어진다. 이런 복제를 줄이기 위해서 몇가지 서브클래스를 생성하고 모든 일반적인 구성 코드를 생성자에 추가한다. 복제 문제를 해결했지만 많은 쓸모없는 서브클래스가 남는다. 프로토타입 패턴은 미리 빌드된 다양하게 구성된 객체의 집합을 프로토 타입으로 사용할 수 있게 한다. 몇가지 구성과 일치하는 서브클래스를 인스턴트화 하는 대신에 클라이언트는 적절한 프로토타입을 찾고 간단히 복제할 수 있다.

장점

  • 구상 클래스와의 결합없이 객체를 복제할 수 있다
  • 미리 빌드된 프로토타입을 복제하기 위해서 반복되는 초기화 코드를 제거할 수 있다
  • 복잡한 객체를 편리하게 생성할 수 있다
  • 복잡한 객체를 위한 구성 프리셋을 처리할 때 상속에 대한 대안을 가질 수 있다

단점

  • 순환 레퍼런스를 가지는 복잡한 객체를 복제하는 것은 까다로울 수 있다. (재귀적으로 구현하는데에는 어려울 수 있음)

구조

  1. 프로토타입 인터페이스로 clone 메소드를 선언한다. 대부분은 clone 메소드 하나
  2. 구상 프로토타입 객체도 clone 메소드를 구현한다. 이 메소드는 원본 객체의 데이터를 복제하는 것 외에도 연결된 객체 복제, 재귀 종속성 해제 등과 관련된 복제 과정의 일부 특이 사례들을 처리할 수 있다.
  3. 클라이언트는 프로토타입 인터페이스를 따라서 객체의 복사본을 생성할 수 있다.

 

 

까지가 

 

Prototype

/ Design Patterns / Creational Patterns Prototype Also known as: Clone Intent Prototype is a creational design pattern that lets you copy existing objects without making your code dependent on their classes. Problem Say you have an object, and you want to

refactoring.guru

에 정리된 내용이다.

프로토타입 패턴은 복사패턴이라고 보면 될 것 같다. 우리는 복사를 하기위해 클래스를 접근해야 하는게 아닌 생성된 객체, 즉, 쓰고있는 객체만 접근을 해서 복사하는 패턴이다. 우리는 각자 쓰고있는 언어에 프로토타입을 구현하기 위해 애먹을 필요는 없다. 물론 예제로 clone 메서드를 담고 있는 인터페이스를 사용한 후 그 안에 있는 필드들까지 복사하는 예제를 따라해볼수는 있겠지만 우리가 쓰고있는 언어들에 프로토타입은 이미 다 구현이 되어있다고 보면 된다.

프로토타입 패턴을 사용할 때 중요한건 얕은 복사깊은 복사를 이해하고 있냐의 차이다

얕은 복사와 깊은 복사는 포인터를 사용하고 있는 객체의 경우 중요하다

 

얕은 복사는 객체안에 들어있는 필드가 주소를 가르키고 있을때 얕은 복사를 하게 되면 두개의 객체는 같은 주소를 바라 보기 때문에
A 의 값이 바뀌게 되면 B 의 값도 같이 바뀌게 된다

 

깊은복사는 포인터의 주소를 다른 값으로 바라 보게 구현된다. 따라서
A 의 값이 바뀌게 되면 B 의 값은 그대로이다

프로토타입을 정말로 golang 으로 구현해보고 싶다면 
아래의 사이트를 방문해서 작성해보면 된다

 

Design Patterns: Prototype in Go

/ Design Patterns / Prototype / Go Prototype in Go Prototype is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes. All prototype classes should have a common interface that makes it possi

refactoring.guru


하지만 말했듯이 언어는 프로토타입을 이미 내포하고 있다
고 언어에서 얕은 복사는 주소를 가지는 객체를 그대로 대입을 하면 얕은 복사가 일어나게 된다.
슬라이스에서, 깊은복사copy 함수를 쓰면되고 
구조체나 나머지는 gob 패키지를 이용한 직렬화 깊은복사를 구현할수 있다.

 

Person 구조체와 구조체를 초기화하는 NewPerson 함수 그리고 깊은 복사 를 gob 패키지를 통해 구현한 Copy 함수를 만들었다.

메인 함수를 보면 위의 깊은 복사는 구현한 Copy () 에서 쓰여지고 얕은 복사는 a 를 단순히 b에 대입 하였다.
결과는 아래와 같다.

깊은 복사의 주소는 다르고 b 의 이름을 변경해도 달라지지만
얖은 복사는 주소가 같기에 b 의 이름을 변경하면 주소를 가리키기에 이름도 같이 변하게 된다.