ポリモーフィズムをできるだけ簡単に説明してください [非公開]
-
03-07-2019 - |
質問
ポリモーフィズムをわかりやすく説明するにはどうすればよいでしょうか?
このテーマに関する多くの情報は、インターネットや書籍で見つけることができます。 型多態性. 。しかし、できるだけ単純にしてみましょう。
解決
これは、私の類似の質問の回答からのものです。次に、疑似C#/ Javaのポリモーフィズムの例を示します。
class Animal
{
abstract string MakeNoise ();
}
class Cat : Animal {
string MakeNoise () {
return "Meow";
}
}
class Dog : Animal {
string MakeNoise () {
return "Bark";
}
}
Main () {
Animal animal = Zoo.GetAnimal ();
Console.WriteLine (animal.MakeNoise ());
}
Main()メソッドは動物のタイプを認識せず、MakeNoise()メソッドの特定の実装の動作に依存します。
他のヒント
2つのオブジェクトが同じメッセージに異なる動作で応答します。送信者は気にする必要はありません。
シンプルなポップ蓋が付いた缶はどれも同じように開きます。
人間であれば、見つけたものはすべて Open() できることを知っています。
開けたとき、すべての缶が同じように動作するわけではありません。
ナッツが入っているものもあれば、飛び出す偽のヘビが入っているものもあります。
結果は缶のタイプ、缶が「CanOfNuts」か「CanOfSnakes」かによって異なりますが、これは缶の開け方には関係ありません。どの缶を開けてもよく、開けた缶のタイプに基づいて決定されるある種の結果が得られることを知っているだけです。
pUnlabedCan->Open();//ナッツを与えるかもしれないし、ヘビを与えるかもしれない。電話してみるまでは分からない
Open() の汎用戻り型は「Contents」です (または、戻り型を指定しないこともできます)。そのため、open は常に同じ関数シグネチャを持ちます。
人間であるあなたがユーザー/発信者です。
Open() は仮想/ポリモーフィック関数です。
「Can」は抽象基本クラスです。
CanOfNuts と CanOfSnakes は、「Can」クラスの多態性の子です。
どの缶も開けられるかもしれないが、具体的には何なのか する そして具体的にはどのような種類の コンテンツ 返される缶は、それがどのような種類の缶であるかによって定義されます。
pUnlabedCan を見たときに分かることは、それを Open() すれば内容が返されるということだけです。その他の動作 (顔にヘビをぶつけるなど) は、特定の Can によって決定されます。
ポリモーフィズムの最も簡単な説明は、 if / switchステートメントを減らす方法であるということです。
また、既存のクラスを変更せずにif / switchステートメント(または他の人のステートメント)を拡張できるという利点もあります。
たとえば、.NETの Stream
クラスを考えます。ポリモーフィズムがなければ、各メソッドが次のようなswitchステートメントを実装する単一の大規模なクラスになります。
public class Stream
{
public int Read(byte[] buffer, int offset, int count)
{
if (this.mode == "file")
{
// behave like a file stream
}
else if (this.mode == "network")
{
// behave like a network stream
}
else // etc.
}
}
代わりに、具体的なタイプ( FileStream
、 NetworkStream
)に基づいて実装を自動的に選択することにより、ランタイムがより効率的な方法で切り替えを行えるようにします。例
public class FileStream : Stream
{
public override int Read(byte[] buffer, int offset, int count)
{
// behave like a file stream
}
}
public class NetworkStream : Stream
{
public override int Read(byte[] buffer, int offset, int count)
{
// behave like a network stream
}
}
Poly:many
形態素:フォーム/形状
俳優対キャラクター(または役割)
リンゴとオレンジはどちらも果物です。果物を食べることができます。したがって、リンゴとオレンジの両方を食べることができます。
キッカー?食べ方が違います!オレンジは皮をむきますが、リンゴは皮をむきません。
実装は異なりますが、最終結果は同じです。果物を食べる。
カモのように歩き、カモのように鳴く場合、カモが必要な場所ならどこでもカモとして扱うことができます。
多態性により、オブジェクトを「見た目」にできます。同じですが、異なる方法で動作します。通常の例は、Speak()メソッドを使用して動物の基本クラスを取得することです。犬のサブクラスはBarkを発し、豚のサブクラスはoinkを発します。
ほとんどの人が使用する5秒の短い回答で、他の開発者がポリモーフィズムに頭を悩ますことができますオーバーロードとオーバーライド
同じ構文、異なるセマンティクス。
最も簡単な説明:複数の種類のオブジェクトに適用できる動詞。
他のすべては、ヒレルが言ったように、単なる解説です。
ポリモーフィズムは、共通の「親」の知識に依存することにより、物事を抽象的に扱っています。 (犬や猫の親として動物のような階層を考えてください。)
たとえば、すべての動物は酸素を呼吸できますが、それぞれ異なる方法で呼吸することができますが、動物が呼吸するための酸素を供給し、犬と猫の両方をサポートする施設を設計できます。
少し余分に、Animalが「抽象」でも、これを行うことができます。識別子(実際の「動物」というものはなく、動物の種類だけです)。
多相性とは、単一の型の場所に複数の型の値を格納することです。
この質問に対する他のほとんどの回答は、執筆時点では、実際にはポリモーフィズムではなく、動的ディスパッチについて説明していることに注意してください。
動的ディスパッチにはポリモーフィズムが必要ですが、その逆は当てはまりません。 JavaやC#に非常によく似ているが、System.Objectにメンバーがない言語を想像できます。値で何かをする前に型キャストが必要になります。この概念的な言語では、ポリモーフィズムがありますが、必ずしも仮想メソッドやその他の動的ディスパッチメカニズムはありません。
動的ディスパッチは、関連しているが明確な概念であり、他のほとんどの回答で十分に説明されています。ただし、通常、オブジェクト指向言語(最初の(「this」または「Self」)引数タイプに基づいて関数を選択する)で機能する方法は、機能する唯一の方法ではありません。 複数のディスパッチも可能です。すべての引数のタイプに選択が適用されます。
同様に、オーバーロードの解決と複数のディスパッチは、互いに正確に類似しています。オーバーロード解決は、静的な型に適用される複数のディスパッチであり、複数のディスパッチは、多態的な場所に格納されているランタイム型に適用されるオーバーロードの解決です。
多態性は、共通のプロパティに基づいて世界をボックスに分割し、これらの共通のプロパティのみを使用する場合に、特定のボックス内のアイテムを交換可能として扱います。
ポリモーフィズムとは、異なるものを同じものであるかのように扱う能力です。
ポリモーフィズムは、同じメソッドが複数のクラスに適用されるときに得られるものです。たとえば、文字列とリストの両方に" Reverse"が含まれる場合があります。メソッド。両方のメソッドの名前は同じです(" Reverse")。どちらの方法も非常によく似た処理を行います(すべての文字を逆にするか、リスト内の要素の順序を逆にします)。しかし、各「逆」の実装メソッドは異なり、そのクラスに固有です。 (つまり、文字列は文字列のように逆になり、リストはリストのように逆になります。)
比metaを使用するには、「夕食を作る」と言うことができます。フランス人シェフまたは日本人シェフに。それぞれが「ディナーを作る」を実行します。独自の方法で。
実際的な結果は、「リバースエンジン」を作成できることです。オブジェクトを受け取り、" Reverse"を呼び出します。その上。オブジェクトにReverseメソッドがある限り、Reverseing Engineは機能します。
シェフの類推を拡張するために、「Waiterbot」を作成できます。シェフに「夕食を作る」ように伝えます。ウェイターボットは、どんな種類の夕食が作られるかを知る必要はありません。シェフと話していることを確認する必要さえありません。重要なのは、「シェフ」が(または消防士、自動販売機、ペットフードディスペンサー)は、「夕食を作る」と言われたときに何をすべきかを知っています。
これがプログラマとしてあなたに買うのは、コードの行数が少なく、タイプセーフか遅延バインディングのどちらかです。たとえば、型の安全性と事前バインディングを使用した例を次に示します(Cに似た言語で作成します):
class BankAccount {
void SubtractMonthlyFee
}
class CheckingAccount : BankAccount {}
class SavingsAccount : BankAccount {}
AssessFee(BankAccount acct) {
// This will work for any class derived from
// BankAccount; even classes that don't exist yet
acct.SubtractMonthlyFee
}
main() {
CheckingAccount chkAcct;
SavingsAccount saveAcct;
// both lines will compile, because both accounts
// derive from "BankAccount". If you try to pass in
// an object that doesn't, it won't compile, EVEN
// if the object has a "SubtractMonthlyFee" method.
AssessFee(chkAcct);
AssessFee(saveAcct);
}
タイプセーフではないが遅延バインディングを使用した例を次に示します。
class DatabaseConnection {
void ReleaseResources
}
class FileHandle {
void ReleaseResources
}
FreeMemory(Object obj) {
// This will work for any class that has a
// "ReleaseResources" method (assuming all
// classes are ultimately derived from Object.
obj.ReleaseResources
}
main() {
DatabaseConnection dbConn;
FileHandle fh;
// You can pass in anything at all and it will
// compile just fine. But if you pass in an
// object that doesn't have a "ReleaseResources"
// method you'll get a run-time error.
FreeMemory(dbConn);
FreeMemory(fh);
FreeMemory(acct); //FAIL! (but not until run-time)
}
優れた例については、.NET ToString()メソッドをご覧ください。すべてのクラスはObjectクラスから派生しているため、すべてのクラスに含まれています。ただし、各クラスは、ToString()をそれ自体に意味のある方法で実装できます。
編集:シンプル!= short、IMHO
ポリモーフィズムは、高レベルのアルゴリズムコードを複数の種類のデータで変更せずに動作させることができる言語機能です。
これは、操作が各データ型に対して適切な実装を呼び出すようにすることで行われます。 (この質問のタグによる)OOPコンテキストでも、この「正しい実装」はコンパイル時または実行時に解決される場合があります(言語が両方をサポートしている場合)。 C ++などの一部の言語では、コンパイラが提供するランタイムポリモーフィズム(仮想ディスパッチ)のサポートはOOPに固有ですが、他のタイプのポリモーフィズムはオブジェクトではないデータ型(つまり、 struct
または class
インスタンスですが、 int
や double
)のような組み込み型の場合があります。
(C ++がサポートするポリモーフィズムのタイプは、私の答えにリストされ、対比されています: c ++のポリモーフィズム-他の言語をプログラムする場合でも、有益な可能性があります)
私がそれを試して考える方法は同じように見えますが、インスタンスに応じて異なる機能を持つことができます。したがって、タイプを持つことができます
interface IJobLoader
ただし、使用方法によっては、同じように見えても機能が異なる場合があります。 BatchJobLoader、NightlyJobLoaderなどのインスタンスがある場合があります
たぶん私は大丈夫です
ポリモーフィズムという用語は、オーバーロード関数にも適用できます。たとえば、
string MyFunc(ClassA anA);
string MyFunc(ClassB aB);
は、オブジェクト指向ではないポリモーフィズムの例です。
オブジェクトが同じメッセージに異なる方法で応答する必要がある能力です。
たとえば、smalltalk、Ruby、Objective-Cなどの言語では、メッセージを送信するだけで応答します。
dao = XmlDao.createNewInstance() #obj 1
dao.save( data )
dao = RdbDao.createNewnewInstance() #obj 2
dao.save( data )
この例では、同じメッセージに異なる方法で応答した2つの異なるオブジェクト:" createNewInstance()とsave(obj)"
同じメッセージに対して、異なる方法で行動します。上記の言語では、クラスが同じクラス階層にない場合もあります。メッセージに応答するだけで十分です。
Java、C ++、C#などの言語。オブジェクトをオブジェクト参照に割り当てるには、インターフェイスを実装するか、共通クラスのサブクラスにすることで、同じ型階層を共有する必要があります。
簡単..かつシンプル。
ポリモーフィズムは、オブジェクト指向プログラミングの最も重要で関連性のある機能です。
これは、さまざまなことを処理する方法であり、その方法を気にせずに、同じような何かを同じ方法で実行できます。
自動車、トラック、スケートボード、飛行機など、さまざまな種類の乗り物が走っているゲームがあるとしましょう。それらはすべて停止できますが、各乗り物は異なる方法で停止します。いくつかの車両はギアをシフトダウンする必要があるかもしれません、そしてあるものはコールドストップに来ることができるかもしれません。ポリモフィズムはあなたにこれをさせます
foreach (Vehicle v in Game.Vehicles)
{
v.Stop();
}
停止の実装方法は異なる車両に委ねられているため、プログラムはそれを気にする必要がありません。
これは、新しいコードを呼び出すために古い風邪をひく方法にすぎません。いくつかの「形状」を受け入れるアプリケーションを作成します。他の人が実装しなければならないメソッドとのインターフェース(例-getArea)。誰かがそのインターフェースを実装するための新しい気まぐれな方法を思いついた場合、古いコードはgetAreaメソッドを介してその新しいコードを呼び出すことができます。
ある種のオブジェクト(例:車)が他のタイプ(例:車両)のように動作(例:ブレーキ)する能力。型階層。
ポリモーフィズムは、関数を別の関数に渡す問題のオブジェクト指向ソリューションです。 Cでは次のことができます
void h() { float x=3.0; printf("%f", x); }
void k() { int y=5; printf("%i", y); }
void g(void (*f)()) { f(); }
g(h); // output 3.0
g(k); // output 5
Cでは、関数が追加のパラメーターに依存する場合は複雑になります。関数hとkが異なるタイプのパラメーターに依存している場合、問題が発生するため、キャストを使用する必要があります。これらのパラメーターをデータ構造に保存し、そのデータ構造へのポインターをgに渡し、hまたはkに渡します。 hおよびkは、ポインターを適切な構造へのポインターにキャストし、データをアンパックします。キャストエラーの可能性があるため、非常に乱雑で非常に危険です:
void h(void *a) { float* x=(float*)a; printf("%f",*x); }
void k(void *a) { int* y=(int*)a; printf("%i",*y); }
void g(void (*f)(void *a),void *a) { f(a); }
float x=3.0;
int y=5;
g(h,&x); // output x
g(k,&y); // output y
したがって、彼らは多型を発明しました。 hとkはクラスに昇格され、実際の関数はメソッドに昇格され、パラメーターはそれぞれのクラスhまたはkのメンバー変数です。関数を渡す代わりに、必要な関数を含むクラスのインスタンスを渡します。インスタンスには独自のパラメーターが含まれています。
class Base { virtual public void call()=0; }
class H : public Base { float x; public void call() { printf("%f",x);} } h;
class K : public Base { int y; public void call() { printf("%i",y);} } k;
void g(Base &f) { f.call(); };
h.x=3.0;
k.y=5;
g(h); // output h.x
g(k); // output k.x