クラスベースのOOPよりもプロトタイプベースのOOPの利点は何ですか?


47

主にクラスベースの言語のコンテキストでOOPを扱った後にJavascriptのプログラミングを始めたとき、プロトタイプベースのOOPがクラスベースのOOPよりも優先される理由について混乱していました。

  1. プロトタイプベースのOOPを使用する場合、構造的な利点はありますか?(たとえば、特定のアプリケーションでより高速であるか、メモリ集約度が低いと予想されますか?)
  2. コーダーの観点からの利点は何ですか?(たとえば、特定のアプリケーションをコーディングしたり、プロトタイピングを使用して他の人のコードを拡張したりするのは簡単ですか?)

この質問を特にJavascriptに関する質問と見なさないでください(長年にわたってプロトタイプ作成とはまったく関係のない多くの欠点がありました)。代わりに、プロトタイピングとクラスの理論的な利点のコンテキストで見てください。

ありがとうございました。


回答:


46

JavaでRPGゲームを作成する際には、両方のアプローチについてかなり多くの経験がありました。もともと私はクラスベースのOOPを使用してゲーム全体を作成しましたが、最終的にはこれが間違ったアプローチであることに気付きました(クラス階層が拡大するにつれて維持できなくなっていました)。したがって、コードベース全体をプロトタイプベースのコードに変換しました。結果は、はるかに優れ、管理しやすくなりました。

興味のある方はこちらのソースコード(Tyrant-Java Roguelike

主な利点は次のとおりです。

  • 新しい「クラス」を作成するのは簡単です -プロトタイプをコピーして、いくつかのプロパティを変更し、新しいクラスを作成するだけです。これを使用して、たとえばそれぞれ3〜6行のJavaで新しいタイプのポーションを定義しました。新しいクラスファイルと定型的な負荷よりもはるかに優れています!
  • 比較的少ないコードで非常に多くの「クラス」を構築および維持することができます。たとえば、Tyrantは、合計で約42,000行のコードで3000種類のプロトタイプのようなものがありました。それはJavaにとっては驚くべきことです!
  • 多重継承は簡単です。1つのプロトタイプからプロパティのサブセットをコピーし、別のプロトタイプのプロパティに貼り付けるだけです。たとえば、RPGでは、「スチールゴーレム」に「スチールオブジェクト」のプロパティの一部、「ゴーレム」のプロパティの一部、および「インテリジェントでないモンスター」のプロパティの一部を持たせることができます。プロトタイプで簡単に、継承階層でそれを試してみてください......
  • プロパティ修飾子を使用して巧妙なことを行うことができます -汎用の「read property」メソッドに巧妙なロジックを入れることにより、さまざまな修飾子を実装できます。たとえば、身に着けている人に+2の力を加える魔法の指輪を定義するのは簡単でした。このロジックは「読み取り強度」メソッドではなくリングオブジェクトにあったため、コードベースの他の場所に多くの条件付きテストを配置する必要はありませんでした(たとえば、「キャラクターは強度リングを着用していますか?」)
  • インスタンスは、他のインスタンスのテンプレートになることができます。たとえば、オブジェクトを「複製」したい場合は、既存のオブジェクトを新しいオブジェクトのプロトタイプとして使用するだけです。さまざまなクラスの複雑なクローニングロジックを大量に記述する必要はありません。
  • 実行時に動作を変更するのは非常に簡単です。つまり、実行時にプロパティを変更し、オブジェクトをほとんど任意に「モーフィング」できます。クールなゲーム内エフェクトを使用できます。これを「スクリプト言語」と組み合わせると、実行時にほとんど何でも可能になります。
  • 「機能的」スタイルのプログラミングにより適しています。特定のクラスにアタッチされたメソッドに埋め込まれたロジックではなく、オブジェクトを適切に分析する多くの関数を記述する傾向があります。私は個人的にこのFPスタイルを好みます。

主な欠点は次のとおりです。

  • 動的なオブジェクトシステムを効果的に作成しているため、静的な型付けの保証が失われます。これは、動作が正しいこと、およびオブジェクトが正しい「種類」であることを確認するために、さらにテストを記述する必要があることを意味する傾向があります
  • いくつかのパフォーマンスオーバーヘッドがあります。オブジェクトプロパティの読み取りは通常、1つ以上のマップルックアップを強制的に実行されるため、パフォーマンスの面でわずかなコストがかかります。私の場合、それは問題ではありませんでしたが、場合によっては問題になる可能性があります(たとえば、すべてのフレームで多くのオブジェクトが照会される3D FPS)
  • リファクタリングは同じようには機能しません。プロトタイプベースのシステムでは、本質的にコードで継承階層を「構築」しています。IDE /リファクタリングツールは、アプローチを理解できないため、実際には役立ちません。これは問題だとは思いませんでしたが、注意しないと手に負えなくなる可能性があります。おそらく、継承階層が正しく構築されていることをテストで確認したいでしょう!
  • それは少し異質です -従来のOOPスタイルに慣れている人々は簡単に混乱するかもしれません。「「Thing」というクラスが1つしかないということですか?!?」-「この最後のThingクラスをどのように拡張しますか!?!」-「あなたはOOPの原則に違反しています!!!」-「あらゆる種類のオブジェクトに作用する静的関数をすべて持つのは間違っています!?!?」

最後に、いくつかの実装ノート:

  • プロパティにはJava HashMapを使用し、プロトタイプには「親」ポインターを使用しました。これは正常に機能しましたが、次の欠点がありました:a)プロパティの読み取りは、長い親チェーンをトレースバックする必要があり、パフォーマンスが低下するb)親プロトタイプを変更した場合、変更は、変更するプロパティをオーバーライドしていないすべての子に影響します 注意を怠ると、微妙なバグが発生する可能性があります!
  • もう一度これを行う場合、プロパティに不変の永続マップ(Clojureの永続マップのようなもの)、または独自のJava永続ハッシュマップの実装を使用します。そうすれば、安価なコピー/変更の利点と不変の動作が得られ、オブジェクトを親に永続的にリンクする必要がなくなります。
  • 関数やメソッドをオブジェクトのプロパティに埋め込んでお楽しみください。このためにJavaで使用したハック(「スクリプト」クラスの匿名サブタイプ)はあまりエレガントではありませんでした-これを再度行う場合は、スクリプト(ClojureまたはGroovy)に適切な簡単に埋め込み可能な言語を使用するでしょう


(+1)その良い分析。Altoughtは、たとえば、Delphi、C#、VB.NetなどのJavaモデルに基づいていますが、明示的なプロパティがあります。
umlcat

3
@umlcat-Javaモデルは、Delphi / C#モデルとほぼ同じであると感じています(プロパティアクセス用の優れた構文糖は別として)-クラス定義で必要なプロパティを静的に宣言する必要があります。プロトタイプモデルのポイントは、この定義が静的ではなく、事前に宣言する必要がないことです
。...– mikera

これは大きなものを逃しました。プロトタイプを変更できます。これにより、プロトタイプのコンストラクターに触れずに作成された後でも、すべてのインスタンスのプロパティを効果的に変更できます。
エリックReppen

多重継承の方が簡単であるという点で、多重継承をサポートしていないJavaと比較しましたが、C ++などの多重継承をサポートする言語と比較すると簡単ですか?
ピオベザン

2

プロトタイプベースのOOPの主な利点は、オブジェクトと「クラス」を実行時に拡張できることです。

クラスベースのOOPには、残念ながらプログラミング言語に依存するいくつかの優れた機能があります。

Object Pascal(Delphi)、VB.NetおよびC#には、プロパティ(フィールドと混同しないでください)およびプロパティのアクセスメソッドを使用する非常に直接的な方法がありますが、JavaおよびC ++のプロパティはメソッドによってアクセスされます。また、PHPには「マジックメソッド」と呼ばれる両方が混在しています。

メインクラスOO言語には静的型付けがありますが、動的型付けクラスがいくつかあります。Class OOを使用した静的型付けは非常に便利だと思います。なぜなら、オブジェクトイントロスペクションと呼ばれる機能を使用すると、これらのIDEを作成し、視覚的かつ迅速にWebページを開発できるからです。


0

@umlcatに同意する必要があります。クラスの拡張は大きな利点です。たとえば、長期間にわたって文字列クラスに機能を追加したいとします。C ++では、以前の文字列クラスの世代を継続的に継承することでこれを行います。このアプローチの問題は、各世代が本質的に異なるタイプになり、既存のコードベースの大規模な書き換えにつながる可能性があることです。プロトタイプ継承を使用すると、元の基本クラスに新しいメソッドを単に「アタッチ」するだけで、継承されたクラスや継承関係をどこにでも大量に構築することはありません。C ++が新しい標準で同様の拡張メカニズムを思い付くのを楽しみにしています。しかし、彼らの委員会は、キャッチーで人気のある機能を追加したい人々によって運営されているようです。


1
Monoliths "Unstrung"を読みたいかもしれません。std::stringすでに独立したアルゴリズムであるか、少なくとも非友人である非会員であるべきメンバーが多すぎます。とにかく、元のクラスを変更できる場合は、in-memory-layoutを変更せずに新しいメンバー関数を追加できます。
デュプリケータ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.