2014년 6월 30일 월요일

javascript 상속(Inheritance) - 의사 클래스 방식과 Object.create 메소드 의 연관성

오전 6:08 Posted by jonnung 1 comment
객체지향 프로그래밍에서 상속은 너무나 자연스럽게 다뤄지는 프로그래밍 기법이다.
상속은 코드 재사용의 한 형태이고, 부모 객체의 데이터 타입 체계를 그대로 전달 받기 때문에 개발 비용을 감소 시켜주는 장점이 있다

하지만 일반적인 객체지향 프로그래밍 언어와 다르게 자바스크립트는 클래스(class)가 없다. 상속(Inheritance) 메커니즘을 구현하기 위해서는 자바스크립트의 특징이라고 할 수 있는 프로토타입(prototype)을 기반으로해서 부모 객체를 자식에게 상속 할 수 있다.
이것은 다시 말해 객체가 다른 객체로 바로 상속 된다는 것을 의미 한다.

이번 포스트에서 정리할 내용은 자바스크립트의 상속 패턴중에서 함수의 프로토타입 프로퍼티를 활용한 방식이다.

지난번 javascript 함수 - 함수 객체의 프로토타입 프로퍼티 이해하기 에서 정리한 함수의 프로토타입 프로퍼티(prototype property)을 먼저 살펴보면 좋을 것 같다.

의사 클래스 방식 Pseudoclassical


의사 클래스(Pseudo class, 슈도 클래스라고 읽음)는 가짜 클래스 정도로 이해하자. 사실 이번에 처음 알게된 표현인데 너무 어렵게 생각했던 것 같다.
자바스크립트에서의 의사 클래스 방식은 클래스가 없는 자바스크립트가 클래스를 흉내내기 위해 작성하는 패턴이라고 생각하면 된다.

자바스크립트는 프로토타입적 특성에 맞게 객체에서 다른 객체로 직접 상속하는 방법을 갖기는 하지만 생성자 함수를 통해서 객체를 생성해야하는 조금 혼란스러운 단계가 있다.

좀 더 자세히 설명하자면, 객체의 프로토타입은 해당 객체가 생성 될때 사용한 객체의 원형에 대한 연결을 담고 있기 때문에 이미 생성된 객체의 프로토타입에 다른 객체를 바로 상속할 수는 없다.
다른 객체를 새로운 객체에 상속하기 위해서는 객체가 생성되기 전에 생성자 함수에서 함수객체의 프로토타입 프로퍼티에 객체를 직접 할당하는 식으로 상속을 구현하는 것이다.
(함수 객체의 프로토타입 프로퍼티에는 자신을 통해 생성될 객체가 참조할 객체의 원형을 갖고 있다.)

자, 그럼 이제 자바스크립트의 상속을 구현하기 위해 의사 클래스 방식으로 예제를 작성해보겠다.

다음 코드는 '여자친구가 당분간 연락하지 말자'고 했을때 당분간의 정확한 기간을 계산해주는 객체를 생성한다.

var Love_anger_formula = function () {
    this.__swear_word_constant = 18;  // 욕주율
    this.get_swear_word_constant = function () {
        return this.__swear_word_constant;
    };
};

var do_not_contact = function (contact_stop_time, lie_count) {
    this.contact_stop_time = contact_stop_time;  // 연락 두절 시간
    this.lie_count = lie_count;  // 거짓말 횟수
};

// do_not_contact 함수의 prototype property 객체를 Love_anger_formula 의 인스턴스로 대체한다.
do_not_contact.prototype = new Love_anger_formula();

var how_long = new do_not_contact(24, 2);

// how_long 객체에 새로운 메소드를 추가한다.
// '당분간'의 기간을 계산한 결과를 돌려준다.
how_long.get_remain_period = function () {
    var  swear_word_constant = this.get_swear_word_constant();
    return this.contact_stop_time * this.lie_count * swear_word_constant;
};

console.log(how_long.get_remain_period());
Do_not_contact 라는 생성자 함수를 정의 하고, 이 함수의 prototype을 Love_anger_formula 의 인스턴스로 대체하는 방식으로 의사 클래스 만들어서 객체를 상속했다.

더글라스 크락포드는 이런 의사 클래스 패턴을 객체지향처럼 보이게 고안 됐지만 마치 어디 외계에서 온 패턴과 같다고 말했다.
자바스크립트에 익숙하지 않는 개발자에게 친숙한 방식이 될 수도 있겠지만, 자바스크립트의 장점을 가리게 되는 방법이라는 말도 덧붙였다.

아무튼 이렇게 생성된 객체의 최대의 문제점은 바로 모든 속성들이 public 이라는 것이다. private은 없다.
how_long.__swear_word_constant로 부모 객체의 속성에 바로 접근해서 읽거나 값을 변경할 수 있다.

또한 생성자 함수를 new 연산자로 실행하지 않으면 this는 새로운 객체에 바인딩 되지 않고, 전역 객체(window)에 연결된다. new 연산자를 빼먹어도 어떠한 에러도 발생하지 않기 때문에 매우 심각한 문제가 발생할 수 있다.

이 문제에 대한 해결책(?)으로 더글라스 크락포드가 추천하는 방법은 2가지가 있는데 new 연산자로 실행해서 객체를 생성하는 생성자 함수의 이름은 첫글자를 대문자로 표기하는 것이다.
그리고 나머지 한가지는 new 연산자 사용을 피하는 것이다.

언어의 특징을 비교하는 차원에서 위 예제 코드와 동일한 기능을 하는 파이썬(python) 코드를 작성해 보았다.
LoveAngerFormula 클래스의 __swear_word_constant 변수는 완전 안전한 private 이다.

# -*- coding: utf8 -*-


class LoveAngerFormula():
    __swear_word_constant = 18  # 욕주율

    def get_swear_word_constant(self):
        return self.__swear_word_constant


class DoNotContact(LoveAngerFormula):
    def __init__(self, contact_stop_time, lie_count):
        self.contact_stop_time = contact_stop_time  # 연락 두절 시간
        self.lie_count = lie_count  # 거짓말 횟수
        self.swear_word_constant = self.get_swear_word_constant()

    def get_remain_period(self):
        """
        '당분간 연락하지 말자' 했을때 당분간의 정확한 기간을 계산
        :return: 기간(일)
        :rtype: integer
        """
        return self.contact_stop_time * self.lie_count * self.swear_word_constant


how_long = DoNotContact(24, 2)
print(how_long.get_remain_period())

Object.create() 전달한 객체를 프로토타입으로 새로운 객체를 생성하는 메소드


Object.create() 메소드로 만들어지는 객체는 프로토타입을 직접 지정해서 생성할 수 있는 편리한 기능을 제공한다.
이런게 있는지도 몰랐었는데 이미 표준에 포함되어 있는 메소드 이다!
크롬(Chrome)과 파이어폭스(Firefox)에서는 당연히 제공하고 있었고, IE는 뒤늦게 9부터 지원한다.

Object.create() 메소드가 생기게 된 배경은 new 연산자 사용이 '자바스크립트 스럽지 못하다'는 의견이 나오게 되면서 new 연산자 사용을 자제하기 위해 만들어졌다고 한다.

그런데 이 메소드를 지원하지 않는 브라우저를 위해서 작성하는 코드를 보면 조금 웃긴 사실을 알 수 있다.

if (typeof Object.create !== 'function' ) {
    Object.create = function (obj) {
        function F();
        F.prototype = obj;
        return new F();
    }
}
결국 Object.create()메소드도 내부적으로 F()라는 빈 함수를 만들고, 위에서 살펴본 의사 클래스 방식으로 F 함수의 프로토타입 프로퍼티에 전달받은 객체를 대체한다.
그리고 new 연산자로 생성한 객체를 돌려준다.

new 연산자 사용을 자제하기 위해서 만들어진 메소드는 결국 손바닥으로 하늘을 가리는 꼴이다.

마무으리


자바스크립트의 프로토타입 특징을 활용한 상속에 대해 정리 해봤는데 조금 찝찝한 느낌을 지울 수 없는 것은 사실이다.
하지만 함수의 프로토타입 프로퍼티로 클래스를 흉내내는 의사클래스방식과 Object.create()메소드의 연관성에 대한 부분은 확실히 알고 간다는 것에 매우 만족한다.




댓글 1개:

  1. 개콘 소재를 코드로 짜다니... 샘플코드가 재미지네ㅋ

    답글삭제