Classless OOP in Javascript


この記事は株式会社ネクスト(Lifull) Advent Calendar 2016の23日目の記事です。
こんにちは、mergyです。普段はHOME'SプライスマップをJavascript、Ruby、Pythonで開発しています。新卒2年目です。
今回はJavaScriptについて書きたいと思います。

はじめに

この記事は何か

Douglas CrockfordがNordic.js 2014で提案した「Classlessオブジェクト指向プログラミング」について解説します。以前からClassの実装に反対していたDouglasがES6のClassを使わないのは当然のことだと言えますが、Classlessオブジェクト指向プログラミングの特徴は、prototypeチェーンすらも使わないことです。

前提知識:Douglas Crockfordとは

(知らない方のために)
Douglas Crockfordは、JSONの発明者、The Good Partsの著者、ECMAScriptの仕様化団体の一員などたくさん肩書があるプログラマーです。
JSONのライセンスに記された「The Software shall be used for Good, not Evil.(邪悪なものには使うな)」という文言からわかるように特徴的な人柄を持っている人物ですし、dcubeioさんのスライドで「ES4の仕様を巡って、Adobe、MozillaがDouglas Crockford、Microsoft、Yahoo!と争った」というような記述があるようにJavaScript界では大きな力を持っていることがわかります。

どこで公開されたか

そのDouglas CrockfordがNordic.js 2014の「The Better Parts」というタイトルでおこなった講演の中でClassless(あるいはClassFree)オブジェクト指向プログラミング)は提案されました。
講演自体はES6のBetter Partsを紹介するのが本旨だったのですが、「Classlessオブジェクト指向プログラミング」自体は既存の考え方を覆すような手法だったために、記事が書かれたり、stack overflowで質問されたりしていました。
(ちなみに何故か日本語で解説している記事が見当たらなかったのでご存知の方いらっしゃいましたら教えてください)
Classlessオブジェクト指向プログラミングに関して上記のGistの記事ではCrockford Classlessという名称が使われていたので、以降はそれに準じます。

Crockford Classless

では実際にコードを見てみましょう。

crockford-classless
function animal(spec) {

  let { name, kind } = spec,
      { say }   = talker({ name }),
      greet = function () {

       if ( kind === 'dog' ) {
          say( 'bow!' );
        } else if ( kind === 'person' ) {
          say('hello!');
        }
      };

  return Object.freeze({
    greet,
    kind
  });

}

function talker(spec) {

  let { name } = spec,
      say = function(sound) {
        console.log(name, "said:", sound)
      }

  return Object.freeze({
    say
  });

}

let hachi = animal({ name: 'Hachi', kind: 'dog' });
let taro  = animal({ name: 'Taro', kind: 'person' });

hachi.greet();
// Hachi said: bow!
taro.greet();
// Taro said: hello!

Crockford Classlessではこのようにオブジェクトの生成を行います。

Crockford Classlessの特徴

Crockford Classlessには下記のような特徴があります。

  • Class構文もprototypeチェーンも使わない
  • なので継承も行わない(行えない)
  • Object.freeze()でimmutableにする

それでは一つずつ詳細に見ていきましょう。

Class構文もprototypeチェーンも使わない

DouglasがClassをbad partsとして使用しないのは理解できるのですが、今回はprototypeチェーンの問題点も指摘しています。
prototypeチェーンを用いると実装を理解するのが難しくなるし、パフォーマンスの問題もあるので、prototypeチェーンは使わないという考え方のようです。

継承を行わない(Composite Over Inheritance)

Crockford Classlessでは継承を行いません。「オブジェクト指向なのに継承をしないの?」と思われるかもしれませんが、これを理解するためには、Composite Over Inheritanceという考え方を知る必要があります。

「Composite Over Inheritance」とは、「継承でなくcomposeパターンを使おう」というオブジェクト指向プログラミングの主義の1つです。composeパターンについてはTechScoreさんの記事などを参考にしていただければ幸いです。
ざっくり言ってしまうと、「親要素から属性を引き継ぐ」という考え方ではなく、「属性を子要素として付与していく」という考え方と捉えてもらって良いかと思います。

「親要素から属性を引き継ぐ」という継承の問題点はいろいろなところに書かれているかと思いますが、Crockford Classlessとの関係で見るならこちらの記事(Class Free Object Oriented Programming)がわかりやすいです。
(「鳥クラスを継承したアヒルは飛べてしまうよ?」「ワニインスタンス作りたいときはどんなクラスを作ればいいの?」「複雑化した継承はキメラを生み出してしますよ」などです)

※画像はいらすとやさんからお借りしました。

今回の例だとtalkerが属性を持つ子要素になります。このように要素だけ切り離しておいて、必要なオブジェクトに付与すれば良いという考え方のようです。

Object.freeze()

Object.freeze()は新しいプロパティの追加、既存のプロパティの削除などを禁止し、オブジェクトを基本的にimmutableにする構文です。

Crockford ClasslessでObject.freeze()を用いててることのメリットは2つあります。
1、スペルミスなどで存在しないプロパティを参照するような一般的なエラーを回避できる
2、オブジェクトを生成した後に、自分や他の開発者がメソッドの追加や再定義を行うことを防ぐことができる

具体的にやってみましょう。

Object.freeze()
hachi.bread = 'Chihuahua';
console.log(hachi.bread);
// undefined

delete hachi.name;
hachi.greet();
// Hachi said: bow!

immutableなObjectが生成されていることがわかります。

Crockford Classlessのメリットまとめ

以上のことをまとめます。

  • composeパターンを用いることで無理な継承やインターフェイス実装を行う必要がなくなる
  • プロパティをimmutableにすることでヒューマンエラーを防げる
  • prototypeチェーンを使用しないことでパフォーマンスを向上させることができる

Crockford Classlessに関しては自分も理解できていない部分が多く、まだまだ勉強が必要です。何か間違いやフィードバックなどあればどんどんコメントいただければ幸いです。
よろしくお願いします。

参考・関連URL

classless.md
https://gist.github.com/mpj/17d8d73275bca303e8d2
Understanding Crockford's classless OOP implementation
http://stackoverflow.com/questions/31615655/understanding-crockfords-classless-oop-implementation
http://bdadam.com/blog/video-douglas-crockford-about-the-new-good-parts.html
Class Free Object Oriented Programming
https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/
Composition over inheritance
https://en.wikipedia.org/wiki/Composition_over_inheritance
Composition versus Inheritance
https://lostechies.com/chadmyers/2010/02/13/composition-versus-inheritance/