[Design Patterns] Prototype 패턴

·2024년 1월 31일
0

patterns

목록 보기
4/11
post-thumbnail

Prototype

Prototype 패턴은 동일 타입의 여러 객체들이 프로퍼티를 공유할 때 유용하게 사용한다.
Prototype은 Javascript 객체의 기본 속성이므로 Prototype 체인을 활용할 수 있다.

하나의 앱을 만들 때 동일한 타입의 여러 객체를 만들어내곤 한다. ES6 클래스의 여러 인스턴스를 만들어낼 때 유용하게 사용할 수 있다.

여러 강아지 클래스를 만들어보자. 예제에서 강아지는 이름을 가지고 있고 짖을 수 있다.

class Dog {
  constructor(name) {
    this.name = name
  }
  
  bark() {
    return 'Woof!'
  }
}

const dog1 = new Dog('Daisy');
const dog2 = new Dog('Max');
const dog3 = new Dog('Spot');

Dog 클래스의 생성자에서는 name 프로퍼티를 가지고 있고 클래스 자체적으로는 bark 프로퍼티를 가지고 있다.
ES6 클래스를 사용하면 모든 프로퍼티는 클래스 자체에 선언되며 위의 코드에서 bark는 자동으로 prototype에 추가된다.

생성자의 prototype 프로퍼티 혹은 생성된 인스턴스의 __proto__ 프로퍼티를 통해 Prototype 객체를 확인할 수 있다.

__proto__는 비표준이므로 __proto__ 대신 Object.getPrototypeOf을 사용하는 것이 좋다.

console.log(Dog.prototype)
// constructor: f Dog(name) bark: f bark()

console.log(dog1.__proto__)
// constructor: f Dog(name) bark: f bark()

어떤 인스턴스던지 __proto__의 값은 Prototype 객체를 가리킨다. 객체에 없는 프로퍼티에 접근하려 하는 경우 Javascript는 이 프로퍼티가 나타날 때까지 prototype chain을 거슬러 올라간다.

Prototype 패턴은 객체들이 같은 프로퍼티를 가져야 하는 경우 유용하게 쓰일 수 있다. 중복된 프로퍼티들이 존재하는 객체를 매번 생성하기 보다, Prototype에 프로퍼티를 추가하면 모든 인스턴스들이 Prototype 객체를 활용할 수 있다.
모든 인스턴스들이 Prototype에 접근할 수 있기 때문에 인스턴스를 만든 뒤에도 Prototype에 프로퍼티를 추가할 수 있다.

예제에서 강아지는 짖기만 가능했지만 놀 수도 있게 구현해보자. Prototype 객체에 play 프로퍼티를 추가하여 가능하다.

class Dog {
  constructor(name) {
    this.name = name
  }
  
  bark() {
    return 'Woof!'
  }
}

const dog1 = new Dog('Daisy');
const dog2 = new Dog('Max');
const dog3 = new Dog('Spot');

Dog.prototype.play = () => console.log("Playing now!");

dog1.play();

Prototype "체인"이란 단어처럼 Prototype은 한 단계 이상도 존재할 수 있다. 지금까지 어떤 인스턴스의 proto 속성에 대해서 이야기했지만, 사실 Prototype 객체 자체도 proto 속성을 가질 수 있다.

이제 다른 타입의 강아지를 만들어 보자. 이 강아지는 Dog의 속성을 모두 가지고 있지만 하늘을 날 수 있다. 이 슈퍼 강아지는 Dog 클래스를 상속받아 fly 메서드를 구현하면 된다.

class SuperDog extends Dog {
  constructor(name) {
    super(name)
  }
  
  fly() {
    return 'Flying!'
  }
}

아래 예제에서는 Daisy라는 강아지를 만들고 짖거나 하늘을 날 수 있게 하고 있다.

class Dog {
  constructor(name) {
    this.name = name
  }
  
  bark() {
    console.log('Woof!')
  }
}

class SuperDog extends Dog {
  constructor(name) {
    super(name)
  }
  
  fly() {
    console.log('Flying!')
  }
}

const dog1 = new SuperDog("Daisy");
dog1.bark();
dog1.fly();

SuperDogDog를 상속했다. 따라서 인스턴스 dog1은 bark 메서드도 호출할 수 있다.
SuperDog의 Prototype 객체의 proto는 Dog.prototype을 가리키고 있다.

Prototype 체인이라고 불리는 이유가 명확해졌다. 현재 객체에 없는 프로퍼티에 접근하려 하는 경우 Javascript는 같은 이름의 프로퍼티를 찾을 때까지 재귀적으로 객체의 proto를 따라 거슬러 올라가게 된다.

Object.create

Object.create 메서드는 Prototype으로 쓰일 객체를 인자로 받아 새로운 객체를 만들어낸다.

const dog = {
  bark() {
    return 'Woof!'
  }
}

const pet1 = Object.create(dog)

pet1 자체적으로는 아무런 프로퍼티도 없지만, dog 객체를 Prototype으로 사용하기 때문에 bark 메서드를 사용할 수 있다.

const dog = {
  bark() {
    console.log(`Woof!`);
  }
};

const pet1 = Object.create(dog);

pet1.bark(); // Woof!
console.log("Direct properties on pet1: ", Object.keys(pet1));
console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));

Object.create는 단순히 객체가 다른 객체로부터 프로퍼티를 상속받을 수 있게 해준다. 실행 결과로 생성되는 객체는 Prototype 체인으로 인해 인자로 넘어갔던 객체의 프로퍼티를 활용할 수 있는 것이다.

Prototype 패턴은 어떤 객체가 다른 객체의 프로퍼티를 상속받을 수 있도록 해준다. Prototype 체인을 통해 해당 객체에 프로퍼티가 직접 선언되어 있지 않아도 되므로 메서드 중복을 줄일 수 있고 이는 메모리 절약으로 이어진다.

< 출처 : https://patterns-dev-kr.github.io/design-patterns/prototype-pattern/ >

profile
개발을 개발새발 열심히➰🐶

0개의 댓글