Prototype[Design Pattern]

SnowCat·2023년 2월 27일
0

Design Pattern

목록 보기
5/23
post-thumbnail

의도

  • 프로토타입 -> 코드를 그들의 클래스에 의존시키지 않고 기존 객체들을 복사할 수 있도록 하는 생성 디자인 페턴

문제

  • 객체가 있고 객체의 정확한 복사본을 만들어야한다 가정해보자.
  • 가장 간단한 방법은 같은 클래스의 새로운 객체를 생성하는 것이다.
  • 하지만 세가지 문제점을 가지고 있음
    1. private 필드를 어떻게 복사할 것인가?
    2. 객체의 클래스를 알아야 함 -> 코드가 해당 클래스에 의존적이게 됨
    3. 메서드의 매개변수가 일부 인터페이스를 따르는 모든 객체를 수락할 때 객체의 실제 클래스를 모를 수 있음

해결책

  • 실제로 복제되는 객체들에 복제 프로세스 위임
    복제를 지원하는 모든 객체에 대한 공통 인터페이스 선언 -> 코드를 객체의 클래스에 결합하지 않고 객체 복제 가능
  • 현재 클래스의 객체를 만들고, 이전 객체의 모든 필드 값을 새 객체로 전달
  • 객체들이 같은 클래스에 속한 다른 비공개 필드에 접근할 수 있기에 비공개 필드를 복사하는 것도 가능
  • 복사를 지원하는 객체를 프로토타입이라 함

구조

// 복제 메서드 선언
// 필요하ㅣ다면 인터페이스를 통해 복제 메서드들을 먼저 선언해줘도 됨
class Prototype {
    public primitive: any;
    public component: object;
    public circularReference: ComponentWithBackReference;
	
  // Object.create() 메서드를 통해 이전 객체의 필드값, 즉 자바스크립트의 프로토타입 전달 가능
    public clone(): this {
        const clone = Object.create(this);
      	// 각각의 속성들에 대해서도 프로토타입 지정
        clone.component = Object.create(this.component);
        clone.circularReference = {
            ...this.circularReference,
            prototype: { ...this },
        };
        return clone;
    }
}

// 정의된 프로토타입을 받는 또다른 클래스
class ComponentWithBackReference {
    public prototype;

    constructor(prototype: Prototype) {
        this.prototype = prototype;
    }
}

//클라이언트 코드
function clientCode() {
    const p1 = new Prototype();
    p1.primitive = 245;
    p1.component = new Date();
    p1.circularReference = new ComponentWithBackReference(p1);
	
  //p2는 p1을 프로토타입을 통해 복제함
    const p2 = p1.clone();
    if (p1.primitive === p2.primitive) {
        console.log('Primitive field values have been carried over to a clone. Yay!');
    } else {
        console.log('Primitive field values have not been copied. Booo!');
    }
    if (p1.component === p2.component) {
        console.log('Simple component has not been cloned. Booo!');
    } else {
        console.log('Simple component has been cloned. Yay!');
    }

    if (p1.circularReference === p2.circularReference) {
        console.log('Component with back reference has not been cloned. Booo!');
    } else {
        console.log('Component with back reference has been cloned. Yay!');
    }

    if (p1.circularReference.prototype === p2.circularReference.prototype) {
        console.log('Component with back reference is linked to original object. Booo!');
    } else {
        console.log('Component with back reference is linked to the clone. Yay!');
    }
}

clientCode();
/*
Primitive field values have been carried over to a clone. Yay!
Simple component has been cloned. Yay!
Component with back reference has been cloned. Yay!
Component with back reference is linked to the clone. Yay!
*/

적용

  • 복사해야 하는 객체들의 구상 클래스에 코드가 의존하면 안될 때 사용
    코드가 인터페이스를 통해 다른 모르는 객체들과 함께 작동할 때 둘을 분리하기 위해 사용
    클라이언트 코드가 복제하는 객체들의 구상 클래스에서 코드를 독립시킴

  • 객체를 초기화하는 방식만 다른 자식 클래스의 수를 줄이고 싶일 때 사용
    다양한 설정으로 미리 만들어진 객체들의 집합을 프로토타입으로 사용해 자식 클래스를 인스턴스화 하지 않고 프로토타입 복제로 문제 해결 가능

구현방법

  1. 프로토타입 인터페이스를 생성한 이후 clone 메서드 선언
    만약 계층 구조가 있으면 clone 메서드를 계층 구조의 모든 클래스에 선언
  2. 프로토타입 클래스 선언, 이 때 클래스 객체를 인수로 받아들이는 대체 생성자를 정의하고, 클래스에 정의된 모든 필드값을 인스턴스로 복사해야 함
  3. 자식 클래스를 변경할 때에는 부모 생성자를 호출해 부모 클래스가 부모 클래스의 비공개 필드들의 복제를 처리하도록 해야 함
  4. 클래스 생성시에는 복제 메서드를 명시적으로 오버라이딩 하고 new 연산자와 함께 자체 클래스 이름을 사용해야 함

장단점

  • 객체들을 구상 클래스에 결합하지 않고 복제 가능
  • 반복되는 초기화 코드를 제거하고, 프로토타입 복제로 대체 가능
  • 복잡한 객체들에 대한 사전설정을 상속대신 처리 가능하기에 더 쉽게 객체 생성이 가능해짐
  • 순환참조가 있는 복잡한 객체를 복제하는 것은 까다로울 수 있음

출처:
https://refactoring.guru/ko/design-patterns/prototype

profile
냐아아아아아아아아앙

0개의 댓글