[번역]간단한 자바스크립트 상속

원본 : http://ejohn.org/blog/simple-javascript-inheritance/

난 최근에 자바스크립트 상속에 관해 많은 일을 해왔다.

다시말해 “자바스크립트 책 안에서의 내 일“을 위해서, 그리고  다수의 다른

자바스크립트의 고전적인 상속 시뮬레이팅 기술에 대해 시험해왔다.

내가 보고 생각한 것 중 가장 좋아하는 것은 “base2“와 “Prototype“에 의해

사용된 구현이었다.

난 이런 기술들에서 간단하고, 재사용하고,어떤 의존성 없이 쉽게 이해할 수 있는 양식

가지는 기술을 뽑아내기를 원했다.

부가적으로, 난 간단하면서도 사용성이 높은 결과가 나오길 원했다.

여기 당신이 그것을 사용할 수 있는 예제가 있다.

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});
var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // Call the inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

var p = new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class

이 구현에 대해 주목해야 할 것들은..

  • 생성자 생성이 간단하다(이 경우 init method의 간단한 제공으로 트릭을 썼다)
  • new ‘class’의 생성을 위해 존재하는 클래스를 확장할 수 있다.
  • 하나의 조상(Class)으로부터 상속받은 모든 ‘classes’들은 클래스이다.
    그러므로, 새로운 클래스를 만들길 원한다면 그것은 Class의 sub-class일 것이다
  • 가장 도전적인 것 : 오버라이드 된 메서드에 접근하는 것도 제공된다
    (그들의 전적인 문맥에 맞춰) 당신은 this._super(), Person super-class에서
    init()과 dance()메서드를 호출하는 것을 사용하는 걸 볼 수 있다.

난 결과를 기쁘게 생각한다. 그것은 구조로서의 “classes”의 개념을 강화하는 데 도움을 줬고

간단한 상속을 유지하고, 슈퍼 메서드 호출을 허용한다.

-간단한 클래스 생성과 상속-

여기 25라인에 걸쳐 꽤나 합리적인 사이즈에 주석이 잘 달린 구현이 있다.

피드백은 환영하고 감사드린다.

/* Simple JavaScript Inheritance
 * By John Resig http://ejohn.org/
 * MIT Licensed.
 */
// Inspired by base2 and Prototype
(function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
  // The base Class implementation (does nothing)
  this.Class = function(){};

  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;

    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;

    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;

            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];

            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);
            this._super = tmp;

            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }

    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }

    // Populate our constructed prototype object
    Class.prototype = prototype;

    // Enforce the constructor to be what we expect
    Class.prototype.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

    return Class;
  };
})();

두개의 교묘한 부분에 대한 내 의견은 “초기화/init을 호출하지 않고“,

_super 메서드를 생성“하는 부분이다.

난 저 부분에 대해 간단히 다루길 원했고, 그래서 이 메서드를 구현하는 것에

대한 잘 이해할 수 있을 것이다.

–  초기화 – 

function prototype과 함께 상속을 모의시험하기 위해 우리는 super-class의

객체를 생성하는 전통적인 기술을 사용했고 그것에 prototype을 할당했다.

그것을 이와 같이 사용할 수 있을 것이다.(?)

function Person(){}
function Ninja(){}
Ninja.prototype = new Person();
// Allows for instanceof to work:
(new Ninja()) instanceof Person

이것에 대한 도전은, Person객체를 설명하는 전체 비용 없이 ‘instanceof’의 이익이

우리가 정말로 원하는 모든 것이다. 우리 코드안의 변수에 대응하기 위해,

initializing(초기화), prototype을 위해 그것을 사용하는 단 하나의 목적과 함께

우리가 클래스  상속을 할때는 언제든지 그것은 맞는 조합이다.

그래서 함수를 정말로 만들 시점에, 우리는 초기화 메서드가 필요하지 않고

정확히는 init method를 실행한다.

if ( !initializing )
  this.init.apply(this, arguments);

이것에 관해 특별히 중요한 것은 init method가 비용이 많이 드는 시작코드의 모든 종류를

실행할 수 있다(서버 연결, DOM 요소 만들기) 그래서 일하는 데 처해지는 어려움을 제법 잘

피하게 된 것이다.

-슈퍼 메서드-

당신이 상속을 할때, 슈퍼 클래스로부터 기능을 상속하는 클래스를 만드는 것은,

자주 쓰이는 것은 당신이 오버라이드 한 메서드에 접근하는 능력이다.

마지막 결과로, 이 특별한 구현안에, 서브 클래스의 메서드 안으로부터

오직 접근가능한, 슈퍼 클래스와 연결된 메서드를 참조하는 새로운

임시 메서드(_super)가 있다.

예를 들면, 슈퍼 클래스의 생성자를 호출하길 원한 경우 이런 기술을

사용할 수 있다.

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  }
});
var Ninja = Person.extend({
  init: function(){
    this._super( false );
  }
});

var p = new Person(true);
p.dancing; // => true

var n = new Ninja();
n.dancing; // => false

이 기능의 구현은 몇가지 스텝이 있다. 시작을 위해, 우리가 이미 존재하는 클래스를

확장해 사용하는(Person.extend와 같은), 새로운 Person 객체의 기초위에 합쳐져야

할 필요하기 있는(이전에 기술된 구조 위에) 객체 리터럴에 주목하라.

우리는 간단한 체크를 한다 : 우리가 기능을 합치는 시도, 그리고 또한 기능을

대체하는 것에 대한 소유가 있는가? 우리는 새로운 슈퍼 메서드가 하는 일을

위한 방법을 만드는 것이 필요하다.

새로운 강화된 슈퍼 메서드를 압축하게 될 익명의 종결(함수가 리턴되는) 을

만든다.  To start we need to be ad good citizen(?), 기존의 this._super에 참조를

저장한다(실제 존재하는 걸 무시하고) 그것은 우리가 한 이후에 회복한다.

이것은 같은 이름이 이미 존재하는(사고로 그것을 떠나보내길 원하지 않는)

변수가 있는 곳에 대한 경우에 도움을 줄 것이다.

다음으로 우리는 슈퍼클래스의 prototype에 존재하는 메서드의 참조를 가진

새로운 _super메서드를 만든다. 고맙게도 우리는 어떤 부가적인 변화를

만들지 않아도 된다. 여기 함수의 문맥처럼 우리 객체(이것은 슈퍼클래스에

반대는 객체를 참조할 것이다)의 소유를 자동으로 조합할 것이다.

마침내 우리는 우리의 원래 메서드를 호출한다. 그것은 자신의 역할을 수행한다

후에 우리는 그것의 원래 상태에 _super를 회복하고 함수로부터 리턴받는다.

지금 간단한 결과를 구현한 대한 다양한 방법이 있다.

(인자들로부터 접근가능하고, 그 자신의 메서드에 슈퍼 메서드의 경계를 가지는

구현을 본) 그러나 난 유용함과 간단함의 최고 조합을 이 기술이 제공했으리라

느낀다.

난 나의 완벽한 일 안에 자바스크립트 prototype system뒤에 nitty-gritty의 많은

것이 담겨져 있을 것이다.

하지만  난 이 클래스 구현으로 부터 그것을 시도하고 구현함에 있어 모든 것을

얻기를 바란다. 나는 이 간단한 코드(배우기 쉽고, 확장하기 쉽고, 다운로드에

부담이 없는)에 많은 것을 말할 수 있으리라 생각한다.

그래서 난 이 구현이 좋은 출발점이 되리라 생각하고 자바스크립트 클래스 구조와

상속의 기본을 배울 수 있다.

Advertisements

[번역]자바 Enum의 10가지 예제

원본글 : http://javarevisited.blogspot.sg/2011/08/enum-in-java-example-tutorial.html

자바의 Enumeration(Enum)은 JDK 1.5에 소개되었고, J2SE5에서 내가 가장 좋아하는

특징 중 하나이다.

타입처럼 Java Enum은 NEW,PARTIAL,FILL,FILL or CLOSED와 같은 주문의

대표적인 상태의 예와같이 확실한 상황하에서 더욱 적합하다.

Enumeration(Enum)은 원래 자바에서 이용하지 않았고,  C나 C++같은 다른 언어에서

사용했다.

하지만 결국 자바는 깨닫게 되었고 enum keyword에 의해 JDK 5안에 Enum이 소개되었다.

이 Java enum 튜토리얼안에서 우리는 자바안의 다른 enum 예를 볼 수 있을 것이고

자바안의 enum 사용에 대해 배울 수 있다.

자바안에 Enum에 의해 공급된 다른 특징들과 그것들을 어떻게 사용해야 되는지가

이 Java enum 튜토리얼의 초점이 될 것이다.

만약 당신이 자바 enum을 불편하게 느끼지 않는 것보다 전의 C 혹은 C++에서

Enumeration을 사용하고 있다해도,자바안의 Enum에 대한 나의 의견은

어느 다른 언어보다도 더 다채롭고 다용도로 쓰일 것이다.

  • Java enum 없이 어떻게 enumberable을 표현하는가

자바 enum이 자바 1.5로부터 사용된 이래로, 그것의 가치는 JDK1.5이전의 자바 안

enumerable value를 어떻게 보여주어서 사용할지 혹은 그것이 없이 사용할지에 대한

부분에 대해서 논의했다.

나는 behavior같은 enum을 모방해서 public static final 상수를 사용했다.

개념을 더욱 명확히 이해하기 위해 자바안의 enum 예제를 살펴보자

이 예제에서 우리는 PENNY (1) NICKLE (5), DIME (10), and QUARTER (25) 같이

값을 가지는 열거형처럼 US 통화코인을 사용할 것이다.

class CurrencyDenom {
            public static final int PENNY = 1;
            public static final int NICKLE = 5;
            public static final int DIME = 10;
            public static final int QUARTER = 25;

      }

class Currency {
   int currency; //CurrencyDenom.PENNY,CurrencyDenom.NICKLE,
                 // CurrencyDenom.DIME,CurrencyDenom.QUARTER
}

이것이 우리의 목적을 어느정도 충족해주긴 하지만 몇가지 심각한 한계를 가지고 있다.

(1)No Type-Safety

모든것중에 첫번째는 타입에 대해 안전하지 않다는 것이다. 당신이 99 통화에 대해

어느 유효한 int value값을 할당하려 해도 저 값을 대표하는 coin이 없다.

(2) No Meaningful Printing 

이 상수에 대한 어느것의 값을 출력하는 것은  당신이 “NICKLE “대신에 “5”를 출력할 때

코인의 의미있는 이름 대신에 그것의 numeric 값을 출력하게 될 것이다

(3) No namespace

currencyDenom 상수에 접근하기 위해 우리는 단지 PENNY를 사용하는 대신에

CurrencyDenom.PENNY 클래스이름을 고쳐서 사용할 필요가 있다.

이것은 또한 JDK 1.5안에서 static import를 사용함으로 이루어지게 되었다.

자바 enum은 이 모든 한계에 대한 대답이다. 자바의 enum은 타입에 대해 안전하고,

의미있는 String 이름을 부여하며 자신의 namaspace를 가지고 있다.

지금 위의 예제를 자바 enum을 사용해서 다시 보자.

public enum Currency {PENNY, NICKLE, DIME, QUARTER};

여기 Currency는 우리의 enum이고 PENNY, NICKLE, DIME, QUARTER는 enum의 상수다.

enum상수 주변에 중괄호를 주목하라. 왜냐하면 enums는 자바의 클래스나 인터페이스

같은 타입이기 때문이다.

또한 우리는 클래스나 인터페이스처럼 enum을 위한 유사한 naming convention을 따를 것이고,

enum 상수들은 static final을 내재한 이후로 우리는 자바안의 상수처럼 그들을 표시하기 위해

모두 대문자로 사용한다.

  • 자바안의 Enum은 무엇인가?

지금 “자바안의 enum은 무엇인가“에 대한 기본적인 질문으로 돌아가는 것이

자바의 키워드이며, 자바 enum에 대한 더욱 자세한 조항은 클래스나 인터페이스와 같은 타입이고

enum 상수로 정의해 사용할 수 있다.

enum 상수에 static과 final이 내재되어 있고, 당신은 한번 만들어지면 값을 바꿀 수 없다.

자바안의 Enum은 타입에 대해 안전하고 int 변수처럼 switch statement안에서 사용된다.

enum은 너가 변수 이름처럼 사용할 수 없는 키워드이며, JDK1.5에 소개된 이후로 너의 모든 이전 코드는

변수 이름과 같은 enum은 돌아가지 않을 것이고 리팩토링할 필요가 있을 것이다.

  • 자바의 Enum에 대한 이익

1) Enum은 타입에 대해 안전하다. 당신은 미리 정의된 enum 변수안의 상수외 다른 것을 할당할 수 없다.

2) Enum은 그 자신의 name-space를 가진다.

3) Enum의 가장 큰 특징은 int나 char같이 원시타입처럼 switch statememt 안에서 Enum을 사용할 수 있다.

4) 자바의 Enum은 새로운 상수를 추가하기가 쉽고, 기존에 존재하는 코드를 고치지 않고 새로운 상수를 추가할

수 있다.

  • 자바의 Enum에 대한 중요한 점

1) 자바안의 Enums는 타입에 대해 안전하고 자신의 name-space를 가진다. 그것은 enum이 type값을 가진다는 것이고

아래 “Currency” 예제에서 Enum 상수안에 명기된 것 외 어떤 다른 값도 할당할 수 없다

public enum Currency {PENNY, NICKLE, DIME, QUARTER};
Currency coin = Currency.PENNY;
coin = 1; //compilation error

2) 지바안의 Enums는 클래스 혹은 인터페이스처럼 타입을 참조한다. 생성자를 정의할 수 있고, 자바 enum안의 메서드나 변수는

자바의 enum type의 다음 예제에서 보여준 것처럼 C나 C++안의 Enum보다 더욱 파워풀하게 만든다

3) 아래 예제에서 보는 것처럼 만들때 enum 상수에 값을 명기할 수 있다.

public enum Currency {PENNY(1), NICKLE(5), DIME(10), QUARTER(25)};

그러나 멤버변수나 생성자를 이용할 필요가 있을 때 이것을 해야한다. 왜냐하면 PENNY(1)은 정말로 아래 예제에서 보는 것처럼

int value를 가지는 생성자를 호출하기 때문이다.

public enum Currency {
        PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
        private int value;

        private Currency(int value) {
                this.value = value;
        }
};

자바 enum의 생성자는 private이고, 다른 접근제어자는 에러가 나올 것이다. 지금 각 코인에 연결된 값을 가져오기 위해 어느 평범한

자바 클래스처럼 자바 enum안에 public getValue()메서드를 정의할 있다. 또한 첫번째 라인의 세미콜론은 선택이다.

4) Enum 상수들은 static과 final을 내재하고 한번 만들어지면 바꿀 수 없다. 아래 코드는 에러를 뱉어낼 것이다

Currency.PENNY = Currency.DIME;
The final field EnumExamples.Currency.PENNY cannot be re assigned.

5) 자바의 Enum은 int, char처럼 switch statement의 인자로 사용할 수 있다. 이런 특징은 switch문에서

매우 유용하다. switch statement에서 java enum을 어떻게 사용하는지 아래 예를 보자

Currency usCoin = Currency.DIME;
    switch (usCoin) {
            case PENNY:
                    System.out.println("Penny coin");
                    break;
            case NICKLE:
                    System.out.println("Nickle coin");
                    break;
            case DIME:
                    System.out.println("Dime coin");
                    break;
            case QUARTER:
                    System.out.println("Quarter coin");
    }

6) enum안에 정의된 상수들은 final이고, 비교할때는 “==”를 사용한다.

Currency usCoin = Currency.DIME;
    if(usCoin == Currency.DIME){
       System.out.println("enum in java can be"+
               "compared using ==");
    }

7) 자바 컴파일러는 자동으로 모든 enum을 위한 static values() 메서드를 제공한다.

Values() 메서드는 enum 상수의 배열을 리턴한다. values()를 사용해서 enums의 값들을

배열을 돌면서 사용할 수 있다.

for(Currency coin: Currency.values()){
        System.out.println("coin: " + coin);
    }
And it will print:
coin: PENNY
coin: NICKLE
coin: DIME
coin: QUARTER

enums안에 정의된 순서와 동일하다는 것에 주목하라

8) Enum은 메서드를 오버라이드 할 수 있다. 아래예제처럼 enum안에 toString()메서드를

오버라이드 해서 enums 상수들을 위한 의미있는 설명을 제공하고 있다.

@Override
  public String toString() {
       switch (this) {
         case PENNY:
              System.out.println("Penny: " + value);
              break;
         case NICKLE:
              System.out.println("Nickle: " + value);
              break;
         case DIME:
              System.out.println("Dime: " + value);
              break;
         case QUARTER:
              System.out.println("Quarter: " + value);
        }
  return super.toString();
 }
};

그리고 여기 그것이 어떻게 보여지는지 나와있다.

Currency usCoin = Currency.DIME;
System.out.println(usCoin);

output:
Dime: 10

9) 두개의 새로운 컬렉션 클래스 EnumMap과 EnumSet은 추가되었고, 자바 enum을 지원하기 위해 컬렉션

패키지에 추가되었다. 이러한 클래스들은 자바의 Map과 Set 인터페이스에서 높은 성능을 보이며 우리는

어떤 기회에서든지 이것을 사용할 수 있다

10) 자바의 new 연산자를 사용해서 객체를 생성할 수 없다. 왜냐하면 Enum의 생성자가 private이고

Enums 상수는 오직 Enums 그 자신 안에서는 만들어질 수 있다.

11) 자바 enums의 객체는 어떤 enums 상수가 코드안에서 처음 호출되거나 참조될 때 만들어진다.

12) 자바의 Enum은 인터페이스를 구현하고 평범한 클래스처럼 어느 메서드라도 오버라이드 할 수 있다.

그것은 또한 Serializable과 Comparable 인터페이스 둘다 구현을 내재하고 있다.

java enum을 사용하여 인터페이스를 어떻게 구현하는지 아래 예를 보자

public enum Currency implements Runnable{
  PENNY(1), NICKLE(5), DIME(10), QUARTER(25);
  private int value;
  ............

  @Override
  public void run() {
  System.out.println("Enum in Java implement interfaces");

   }
}

13) 자바의 enum안에 추상메서드도 정의할 수 있다. 또한 enum의 다른 객체를 위해 다른

구현을 제공할 수도 있다. 아래 enum안에 추상메서드를 사용한 예제를 보자

public enum Currency implements Runnable{
          PENNY(1) {
                  @Override
                  public String color() {
                          return "copper";
                  }
          }, NICKLE(5) {
                  @Override
                  public String color() {
                          return "bronze";
                  }
          }, DIME(10) {
                  @Override
                  public String color() {
                          return "silver";
                  }
          }, QUARTER(25) {
                  @Override
                  public String color() {
                          return "silver";
                  }
          };
          private int value;

          public abstract String color();

          private Currency(int value) {
                  this.value = value;
          }
          ..............
  }

이번 예에서 모든 코인은 color() 추상메서드를 만들어 다른 컬러를 가지게 될 것이고, 자신의 컬러가

정의된 enum 객체를 가지게 될 것이다.

color()메서드를 호출해서 어느 코인의 컬러를 아래예처럼 가져올 수 있다.

System.out.println("Color: " + Currency.DIME.color());
  • Enum valueof의 예

나의 구독자 중에 한명이 enum에서 String을 바꾸는valueOf메서드에 대해 언급되지 않았다는 걸 알려주었다.

여기 그가 제안한게 있다.

“enum의 valueOf()메서드를 포함할 수 있다. valueOf()는 static 메서드이고 String 인자값을 가지고

enum안에 String을 바꿔 사용할 수 있다.

한가지 생각해야 할건 enum의 valueOf(String)메서드는

“Exception in thread “main” java.lang.IllegalArgumentException: No enum const class

예외를 던질수 있다는 것이다.

ordenal()과 name() 유틸리티 메서드에 제안한 또 다른 독자는 Ordinal은 enum 상수의 포지션(순서)

를 리턴해주고, 특별한 enum 상수를 만들때 정확한 스트링을 리턴하도록 name()이 선언되어 있다라고 한다

*자바 enum의 대해선 아래 블로그에 정리가 잘 되어 있으니 참고하세요

http://iilii.egloos.com/4343065/