ポリモーフィズムが実際のプロジェクトでどのように使用されているかを理解しようとしていますが、Animal
親クラスとmethodspeak()
、およびこのメソッドをオーバーライドする多くの子クラス。これで、任意の子オブジェクトでメソッドspeak()
を呼び出すことができます。次に例を示します。
Animal animal;
animal = dog;
animal.speak();
animal = cat;
animal.speak();
Stream は、ポリモーフィズムの良い例です。
ストリームは、「読み取りまたは書き込みが可能なバイトのシーケンス」を表します。ただし、このシーケンスは、ファイル、メモリ、またはさまざまな種類のネットワーク接続から発生する可能性があります。または、既存のストリームをラップし、暗号化や圧縮などの方法でバイトを変換するデコレータとして機能することもできます。
このように、Streamを使用するクライアントは、バイトがどこから来たかを気にする必要はありません。それらを順番に読み取ることができるというだけです。
Stream
は、ポリモーフィズムの間違った例であると言う人もいます。これは、ネットワークストリームが読み取りまたは書き込みのみを許可し、同時に両方は許可しないなど、実装者がサポートしない多くの「機能」を定義するためです。またはシークの欠如。ただし、Stream
は独立して実装できる多くの部分に分割できるため、これは複雑さの問題にすぎません。
典型的なゲーム関連の例は、基本クラスEntity
で、draw()
やupdate()
などの一般的なメンバーを提供します。
より純粋なデータ指向の例では、共通のsaveToStream()
およびloadFromStream()
を提供する基本クラスSerializable
が存在する可能性があります。
ほとんどのUIツールキットには、多くの継承と多態性があります。
たとえば、JavaFX UIツールキットでは、 Button
はButtonBase
から継承します。これはLabeled
から継承します。 Control
から継承Region
から継承Parent
から継承Node
はObject
から継承多くのレイヤーは、以前のもののいくつかのメソッドをオーバーライドします。
そのボタンを画面に表示したい場合は、それをPane
に追加します。これにより、Node
から継承するすべてのものを子として受け入れることができます。しかし、どのようにしてペインがボタンを一般的なNodeオブジェクトと見なすだけの場合にボタンで何をするかを知るのですか?そのオブジェクトは何でもかまいません。ボタンは、 Nodeボタン固有のロジックを使用します。ペインは、Nodeで定義されたメソッドを呼び出し、残りをオブジェクト自体に残します。これは、適用された多態性。
UIツールキットは実世界で非常に重要であり、学術的および実用的な理由の両方で教えるのに役立ちます。
ただし、UIツールキットにも重大な欠点があります。それらはhugeになる傾向があります。初心者のソフトウェアエンジニアが一般的なUIフレームワークの内部動作を理解しようとすると、多くの場合100以上のクラスに遭遇し、そのほとんどが非常に難解な目的。 「一体何ですか ReadOnlyJavaBeanLongPropertyBuilder
?それは重要ですか?それが何であるかを理解するためにhaveを行いますか?ために?"初心者はそのうさぎの穴で簡単に迷子になることがあります。したがって、彼らは恐怖から逃げるか、構文を学ぶだけの表面にとどまり、実際に内部で何が起こっているかについてあまり考えないようにするでしょう。
ポリモーフィズムにはさまざまな種類があり、関心のあるものは通常、実行時ポリモーフィズム/動的ディスパッチです。
ランタイムポリモーフィズムの非常に高レベルの説明は、メソッド呼び出しは、引数のランタイムタイプに応じて異なる処理を実行することです。オブジェクト自体がメソッド呼び出しの解決を担当します。これにより、柔軟性が大幅に向上します。
この柔軟性を使用する最も一般的な方法の1つは、依存性注入の場合です。別の実装に切り替えたり、テスト用にモックオブジェクトを挿入したりできるようにします。可能な選択肢の数が限られていることを事前に知っている場合、条件付きでそれらをハードコード化しようとすることができます。
_void foo() {
if (isTesting) {
... // do mock stuff
} else {
... // do normal stuff
}
}
_
これにより、コードがわかりにくくなります。代わりの方法は、そのfoo操作のインターフェースを導入し、そのインターフェースの通常の実装とモック実装を記述し、実行時に必要な実装に「注入」することです。 「依存性注入」は「正しいオブジェクトを引数として渡す」ための複雑な用語です。
実際の例として、私は現在、親切な機械学習の問題に取り組んでいます。予測モデルを必要とするアルゴリズムがあります。しかし、私はさまざまな機械学習アルゴリズムを試してみたいと思います。そこで、インターフェースを定義しました。予測モデルには何が必要ですか?いくつかの入力サンプルがある場合、予測とそのエラー:
_interface Model {
def predict(sample) -> (prediction: float, std: float);
}
_
私のアルゴリズムは、モデルをトレーニングするファクトリ関数を取ります:
_def my_algorithm(..., train_model: (observations) -> Model, ...) {
...
Model model = train_model(observations);
...
y, std = model.predict(x)
...
}
_
これで、モデルインターフェイスのさまざまな実装があり、それらを相互にベンチマークできます。これらの実装の1つは、実際には他の2つのモデルを取り、それらをブーストモデルに結合します。このインターフェースのおかげで:
ポリモーフィズムの古典的な使用例はGUIです。 Java AWT/Swing /…のようなGUIフレームワークでは、コンポーネントが異なります。コンポーネントのインターフェース/基本クラスは、画面に自分自身をペイントする、マウスクリックに反応するなどのアクション多くのコンポーネントは、サブコンポーネントを管理するコンテナです。このようなコンテナはどのようにして自分自身を描画するのでしょうか。
_void Paint(Graphics g) {
super.Paint(g);
for (Component child : this.subComponents)
child.Paint(g);
}
_
ここで、コンテナは、サブコンポーネントの正確なタイプを事前に知っている必要はありません。それらがComponent
インターフェースに準拠している限り、コンテナは単にポリモーフィックPaint()
メソッドを呼び出すことができます。これにより、AWTクラス階層を任意の新しいコンポーネントで自由に拡張できます。
ソフトウェア開発全体を通じて繰り返し発生する多くの問題があり、それらは技術としてポリモーフィズムを適用することで解決できます。これらの繰り返し発生する問題とソリューションのペアは設計パターンと呼ばれ、それらの一部は同じ名前の本に収集されています。その本に関して言えば、私の注入された機械学習モデルは戦略であり、「アルゴリズムのファミリーを定義し、それぞれをカプセル化し、そしてそれらを交換可能にします。」コンポーネントがサブコンポーネントを含むことができるJava-AWTの例は、compositeの例です。
ただし、すべての設計でポリモーフィズムを使用する必要があるわけではありません(単体テストで依存性注入を有効にする以外に、これは本当に良い使用例です)。それ以外の場合、ほとんどの問題は非常に静的です。結果として、クラスとメソッドは、多くの場合、ポリモーフィズムには使用されませんが、単に便利な名前空間として、およびかなりのメソッド呼び出し構文に使用されます。例えば。多くの開発者は、ほぼ同等の関数呼び出しaccount.getBalance()
よりもAccount_getBalance(account)
のようなメソッド呼び出しを好みます。これは完全に優れたアプローチであり、多くの「メソッド」呼び出しがポリモーフィズムとは何の関係もないということです。
ここにはすでに素敵な例がありますが、別の例は動物をデバイスに置き換えることです:
Device
はpowerOn()
、powerOff()
、setSleep()
にすることができ、getSerialNumber()
にすることもできます。SensorDevice
はこれをすべて実行でき、getMeasuredDimension()
、getMeasure()
、alertAt(threashhold)
、autoTest()
などの多態性関数を提供します。getMeasure()
は、温度センサー、光検出器、音検出器、または体積センサーに対しては同じ方法で実装されません。そしてもちろん、これらのより特化したセンサーのそれぞれが、いくつかの追加機能を利用できる場合があります。プレゼンテーションは非常に一般的なアプリケーションであり、おそらく最も一般的なものはToString()です。基本的には、Animal.Speak()です。オブジェクトにそれ自体を明示するように指示します。
より一般的に言えば、あなたはオブジェクトに「そのことをする」ように言います。保存、読み込み、初期化、破棄、ProcessData、GetStatusについて考えてみてください。
ポリモーフィズムの私の最初の実用的な使用法は、Javaでのヒープの実装でした。
最大クラスと最小ヒープの違いがメソッド比較の動作方法のみになる、メソッドinsert、removeTopの実装を持つ基本クラスがありました。
abstract class Heap {
abstract boolean compare ( int x , int y );
boolean insert(int x ) { ... }
int removeTop() { ... }
}
MaxHeapとMinHeapが必要な場合は、継承を使用するだけで済みます。
class MaxHeap extends Heap {
MaxHeap(int maxSize) {super(maxSize);}
@Override
boolean compare(int x, int y) {
return x>y; // x<y for minHeap
}
}
バブルソート、挿入ソート、クイックソート、ヒープソートなど、利用可能な多くのソートアルゴリズムがあり、それらは複雑さが異なり、使用するのに最適なアルゴリズムはさまざまな要因に依存します(例:配列のサイズ)
ソートインターフェイスを備えたクライアントは、配列を入力として提供し、ソートされた配列を受け取ることのみを考慮します。特定の要因に応じて、実行時に適切な並べ替えの実装を使用できます。これは、ポリモーフィズムが使用される実際の例の1つです。
上記で説明したのは実行時のポリモーフィズムの例ですが、メソッドのオーバーロードはコンパイル時のポリモーフィズムの例であり、コンパイラーはi/pおよびo/pパラメータータイプとパラメーターの数に応じて、コンパイル時に呼び出し元を正しいメソッドにバインドします。
これが明確になることを願っています。