본문 바로가기
디자인패턴GoF/생성패턴

자바스크립트로 이해해보는 프로토타입패턴 (Prototype Pattern) In 생성패턴

by 봉이로그 2023. 10. 29.

프로토타입패턴 (Prototype Pattern)

 

프로토타입패턴은 객체를 생성할때, 기존 객체를 복사하여 생성하는 패턴이다.

 

즉 기존객체를 사용하여 새로운 객체를 만들어낸다.

 

새 인스턴스를 만드는것보다 기존 객체를 복사하는게 비용이 더 효율적이라고 한다.

 

즉 생산비용을 줄이기 위해 사용한다고 말할 수 있다.

 

학교를 짓는다고 가정할때, 기초적인 교실들의 형태가 만들어져있겠지만, 학교마다 교실들의 갯수나 속성 등이 다를수 있다.

 

그럴경우 이미 생성된 학교객체를 clone하여 교실들을 확장하고 관리할수있도록 하기위해서 프로토타입 패턴을 사용할수도 있다.

 

// Prototpye.java

import java.util.ArrayList;
import java.util.List;

class Classroom implements Cloneable {
    private String name;

    public Classroom(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public Classroom clone() {
        try {
            return (Classroom) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

class School implements Cloneable {
    private List<Classroom> classroomList;

    public School(List<Classroom> classrooms) {
        this.classroomList = classrooms;
    }

    public void addClassroom(Classroom classroom) {
        classroomList.add(classroom);
    }

    public List<Classroom> getClassroomList() {
        return classroomList;
    }

    @Override
    public School clone() {
        List<Classroom> copiedClassrooms = new ArrayList<>();
        for (Classroom classroom : classroomList) {
            copiedClassrooms.add(classroom.clone());
        }
        return new School(copiedClassrooms);
    }
}

public class Prototype {
    public static void main(String[] args) {
        // 초기 교실 목록을 생성
        List<Classroom> classrooms = new ArrayList<>();
        classrooms.add(new Classroom("1학년 1반"));
        classrooms.add(new Classroom("2학년 3반"));
        classrooms.add(new Classroom("3학년 2반"));

        // 초기 학교 생성
        School school = new School(classrooms);

        // 학교를 복제하여 새로운 학교 생성
        School newSchool = school.clone();

        // 새로운 학교에 교실 추가
        newSchool.addClassroom(new Classroom("4학년 1반"));
        newSchool.addClassroom(new Classroom("5학년 4반"));

        // 초기 학교와 새로운 학교의 교실 목록 출력
        System.out.println("초기 학교의 교실 목록:");
        for (Classroom classroom : school.getClassroomList()) {
            System.out.println(classroom.getName());
        }

        System.out.println("새로운 학교의 교실 목록:");
        for (Classroom classroom : newSchool.getClassroomList()) {
            System.out.println(classroom.getName());
        }
        
        /**
        초기 학교의 교실 목록:
        1학년 1반
        2학년 3반
        3학년 2반
        새로운 학교의 교실 목록:
        1학년 1반
        2학년 3반
        3학년 2반
        4학년 1반
        5학년 4반
        */
    }
}

 

인스턴스를 생성할때, new 키워드나 생성자를 통해 객체를 생성하는것이 아니라, 기존에 만들어져 있던

 

인스턴스를 clone하여 객체를 생성한다.

 

자바의 경우 cloneable 인터페이스를 활용해서 clone 메소드를 오버라이딩하여 객체를 생성하고 반환하여 구현을 한다.

 

자바스크립트에서 프로토타입 이라는 개념이 존재한다. 그 개념에 대해 간단하게 알아보면 다음과 같다.

 

자바스크립트에서 프로토타입은 Prototype Object 와 Prototpye Link를 묶어서 지칭한다.

 

function Person(name) {
    this.name = name;
}

// Prototype Object의 sayHello속성에 출력함수를 추가한다.
Person.prototype.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
}

const john = new Person("John");

// Prototype Link를 통해 sayHello 출력함수를 상속하여 사용한다.
john.sayHello(); // 출력: Hello, my name is John

 

자바스크립트에서 프로토타입은 자신이 다른 객체의 원형이 되는 객체이다. 즉 새로운 객체는 해당 프로토타입 객체를 기반으로 생성된다. (최상위 객체는 Object)

 

모든 객체는 프로토타입 객체에 접근 할 수 있다. prototpye 이라는 내부 속성을 가지고 있다.

 

이를 통해 프로토타입 객체에 접근 가능하며, 프로토타입 객체에 값을 추가할 수 있다.

 

프로토타입 객체에 속성이나, 메소드를 추가하면 해당 프로토타입을 기반으로

 

생성된 객체들은 추가된 속성이나, 메소드를 공유하고 사용할수 있다. 

 

 

아래의 코드는 자바로 구현된 코드를 typescript로 변경해보았다.

// Prototpye.ts

class Classroom {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

class School {
    private classroomList: Classroom[];

    constructor(classrooms: Classroom[]) {
        this.classroomList = classrooms;
    }

    get getClassroomList() {
        return this.classroomList;
    }

    addClassroom(classroom: Classroom) {
        this.classroomList.push(classroom);
    }

    clone(): School {
        // 깊은 복사를 위해 각 교실을 새로운 객체로 만들어 복제
        const copiedClassrooms: Classroom[] = this.classroomList.map(classroom => new Classroom(classroom.name));
        return new School(copiedClassrooms);
    }
}

const classrooms: Classroom[] = [
    new Classroom("1학년 1반"),
    new Classroom("2학년 3반"),
    new Classroom("3학년 2반"),
];

const school = new School(classrooms);

const newSchool = school.clone();
newSchool.addClassroom(new Classroom("4학년 1반"));
newSchool.addClassroom(new Classroom("5학년 4반"));

console.log('초기 학교의 교실 목록:');
for (const classroom of school.getClassroomList) {
    console.log(classroom.name);
}

console.log('새로운 학교의 교실 목록:');
for (const classroom of newSchool.getClassroomList) {
    console.log(classroom.name);
}

/**
 * 초기 학교의 교실 목록:
    1학년 1반
    2학년 3반
    3학년 2반
    새로운 학교의 교실 목록:
    1학년 1반
    2학년 3반
    3학년 2반
    4학년 1반
    5학년 4반
 */

 

 

 

 

자바스크립트는 프로토타입 기반의 언어이기때문에, 객체를 생성하게되면 프로토타입 패턴이 적용이 되어지게 된다.

 

즉, 자바스크립트는 프로토타입 패턴이 이미 적용되어졌다고 볼수있다.

 

ES5에 추가된, Object.create() 메소드를 이용해서 구현할수 있다.

 

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

const pet1 = Object.create(dog);

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

/* 
    Woof!
    Direct properties on pet1:  [ 'name' ]
    Properties on pet1's prototype:  [ 'bark' ]
*/

 

Object.create를 사용하지 않는 코드

const animalPrototype = {
    init(_type) {
        this.type = _type;
    },
    getAnimal: function () {
        console.log("The type of this animal is.." + this.type);
    },
    bark() {
        console.log(`Oooo!`);
    }
}

function animal(type) {
    function F() { };
    // 일급 클래스 펑션의 prototype을 animalPrototype객체로 정의 

    // 상단 이미지에서 Foo의 프로퍼티인 propertype 변수에 Foo.prototype 객체를 참조한다

    F.prototype = animalPrototype;

    // new를 하면 animalPrototype객체가 f 변수에 할당 된다
    var f = new F();
    f.init(type);
    return f;
}

const dog1 = animal("dog");

dog1.name = 'happy';

console.log(dog1);

/*
  { type: 'dog', name: 'happy' }
*/

 

 

Reference

https://mobicon.tistory.com/356

 

[JavaScript] Design Pattern - Prototype Pattern

자바스크립트의 상속과 성능향상을 위하여서는 Prototype Pattern을 갖는 Prototypal Inheritance를 이용한다. 개념 - ECMAScript 5에서 Object.create 로 가능하다 var myCar = { name: "Ford Escort", drive: function () { console.l

mobicon.tistory.com

https://seunghun-kim.gitbooks.io/learning-javascript-design-patterns/content/09.javascript-design-patterns/the-prototype-pattern.html

https://www.patterns.dev/posts/prototype-pattern

 

Prototype Pattern

Share properties among many objects of the same type

www.patterns.dev