ポリモーフィズムをわかりやすく説明するにはどうすればよいですか?
Type polymorphismのように、インターネットや本でこの主題に関する多くの情報を見つけることができます。しかし、できるだけシンプルにしましょう。
これは私の似た質問からの answer です。擬似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」であったかどうか、結果は缶の種類に依存しますが、これはあなたがそれを開く方法とは関係ありません。缶を開くことができることを知っているだけで、開けた缶のタイプに基づいて決定されるある種の結果が得られます。
pUnlabledCan-> Open(); //ナッツを与えるかもしれないし、ヘビを与えるかもしれない呼ぶまでわからない
Open()には「Contents」の一般的な戻り値の型(または戻り値の型を決定しない場合があります)があるため、openには常に同じ関数シグネチャがあります。
あなた、人間はユーザー/呼び出し元です。
Open()は仮想/ポリモーフィック関数です。
「Can」は抽象基本クラスです。
CanOfNutsとCanOfSnakesは、「Can」クラスの多態的な子です。
すべてのCanを開くことができますが、具体的にはdoesと、返されるcontentsの具体的なタイは、どのような種類のCanであるかによって定義されます。
pUnlabledCanが表示されたときに知っているのは、Open()を実行してコンテンツを返すことだけです。その他の動作(ヘビの飛び出しなど)は、特定のCanによって決定されます。
ポリモーフィズムの最も簡単な説明はif/switchステートメントを減らす方法ですです。
また、既存のクラスを変更せずにif/switchステートメント(または他の人のステートメント)を拡張できるという利点もあります。
たとえば、.NETのStream
クラスを考えてみます。ポリモーフィズムがなければ、各メソッドが次のようなスイッチステートメントを実装する単一の大規模なクラスになります。
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
}
}
ポリ:多く
モルフィズム:フォーム/シェイプ
リンゴとオレンジはどちらも果物です。果物を食べることができます。したがって、リンゴとオレンジの両方を食べることができます。
キッカー?食べ方が違う!オレンジは皮をむきますが、リンゴは皮をむきません。
したがって、実装は異なりますが、最終結果は同じです果物を食べる。
俳優対キャラクター(または役割)
アヒルのように歩き、アヒルのようにガクガク歩く場合、アヒルが必要な場所ならどこでもアヒルとして扱うことができます。
ポリモーフィズムにより、オブジェクトは同じように「検索」できますが、動作は異なります。通常の例では、Speak()メソッドを使用して動物の基本クラスを取得します。犬のサブクラスは樹皮を放出し、豚のサブクラスは卵を放出します。
ほとんどの人が使用する5秒の短い答えは、他の開発者が多態性に頭を悩ますことができるようにするため、多重定義とオーバーライドを行っています。
同じ構文、異なるセマンティクス。
それを説明する最も簡単な方法:複数の種類のオブジェクトに適用できる動詞。
ヒレルが言ったように、他のすべては単なる解説です。
ポリモーフィズムとは、共通のプロパティに基づいて世界をボックスに分割し、これらの共通のプロパティのみを使用したい場合に、特定のボックス内のアイテムを交換可能として扱うことです。
ポリモーフィズムは、共通の「親」の知識に依存することにより、物事を抽象的に処理します(犬や猫の親としてのアニマルのような階層を考えてください)。
たとえば、すべての動物は酸素を呼吸できますが、それぞれが異なる方法で呼吸を行う場合でも、動物が呼吸するための酸素を供給して犬と猫の両方をサポートする施設を設計できます。
少し余分ですが、Animalは「抽象的な」識別子ですが、これを行うことができます(実際の「Animal」というものは存在せず、単なる動物のタイプです)。
ポリモーフィズムは、同じメソッドが複数のクラスに適用されるときに得られるものです。たとえば、文字列とリストの両方に「逆」メソッドがある場合があります。どちらの方法にも同じ名前(「逆」)があります。どちらの方法も非常によく似た処理を行います(すべての文字を逆にするか、リスト内の要素の順序を逆にします)。ただし、各 "Reverse"メソッドの実装は異なり、そのクラスに固有です。 (言い換えれば、文字列は文字列と同じように反転し、リストはリストと同じように反転します。)
比喩を使うと、フランス人シェフまたは日本人シェフに「Make Dinner」と言うことができます。それぞれが独自の方法で「夕食を作る」ことを行います。
実際の結果として、オブジェクトを受け入れ、そのオブジェクトに対して「リバース」を呼び出す「リバースエンジン」を作成できます。オブジェクトにReverseメソッドがある限り、リバーシングエンジンは機能します。
シェフのアナロジーを拡張するには、シェフに「ディナーを作る」ように指示する「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()を実装できます。
編集:シンプル!=短い、私見
ポリモーフィズムとは、異なるのことをあたかも同じであるかのように扱い、それらの間で共有IDを確立して利用することです。
ポリモーフィズムは、単一の型の場所に複数の型の値を格納することです。
この質問に対する他のほとんどの回答は、私の執筆時点では、実際にはポリモーフィズムではなく動的ディスパッチについて説明していることに注意してください。
ダイナミックディスパッチ にはポリモーフィズムが必要ですが、その逆は当てはまりません。 JavaまたはC#に非常に似ているが、System.Objectにメンバーがいない言語を想像できます。値で何かを行う前に型キャストが必要になります。この概念的な言語では、ポリモーフィズムがあります。ただし、必ずしも仮想メソッドやその他の動的ディスパッチメカニズムではありません。
動的ディスパッチは関連していますが、別の概念であり、他のほとんどの回答で十分に説明されています。ただし、オブジェクト指向言語で最初に機能する方法(最初の( 'this'または 'Self')引数タイプに基づいて関数を選択する)が機能する唯一の方法ではありません。 マルチディスパッチ も可能であり、選択はすべての引数のタイプに適用されます。
同様に、オーバーロードの解決と複数のディスパッチは、互いにまったく同じです。オーバーロードの解決は、静的型に適用される複数のディスパッチですが、複数のディスパッチは、ポリモーフィックな場所に格納されているランタイム型に適用されるオーバーロードの解決です。
ポリモーフィズムは、高レベルのアルゴリズムコードが複数のタイプのデータを変更せずに動作できるようにする言語機能です。
これは、操作が各データ型の正しい実装を呼び出すようにすることで行われます。 OOPコンテキスト(この質問のタグによる))でも、この「正しい実装」はコンパイル時または実行時に解決されます(言語が両方をサポートしている場合)。 C++、ランタイム提供のポリモーフィズム(つまり、仮想ディスパッチ)に対するコンパイラー提供のサポートはOOPに固有ですが、他のタイプのポリモーフィズムは、オブジェクトではない(つまり、struct
やclass
インスタンスですが、int
やdouble
などの組み込み型の場合もあります。
(C++がサポートするポリモーフィズムのタイプがリストされ、私の回答に対比されています: C++でのポリモーフィズム -他の言語をプログラムしていても、それは潜在的に有益です)
私が試して考える方法は同じに見えますが、インスタンスによって機能が異なる可能性があります。だからあなたはタイプを持つことができます
interface IJobLoader
しかし、それがどのように使用されるかに応じて、同じように見えながら、異なる機能を持つことができます。 BatchJobLoader、NightlyJobLoaderなどのインスタンスがある場合があります。
多分私は道を外れています。
あるタイプのオブジェクト(例:車)が別のタイプ(例:車両)の1つのように振る舞う(例:車両)能力は、タイプ階層のある時点で共通の祖先(例:車は車両のサブタイプ)を示唆します。 。
これは、古いコードを呼び出して新しいコードを呼び出す方法にすぎません。あなたは他の人が実装しなければならないメソッド(例-getArea)を持ついくつかの "Shape"インターフェースを受け入れるいくつかのアプリケーションを書きます。誰かがそのインターフェースを実装する新しい方法を思いついた場合、古いコードはgetAreaメソッドを介してその新しいコードを呼び出すことができます。
ポリモーフィズムという用語は、関数のオーバーロードにも適用できます。例えば、
string MyFunc(ClassA anA);
string MyFunc(ClassB aB);
は、ポリモーフィズムの非オブジェクト指向の例です。
それは同じように何かをすることができるさまざまなことを、彼らがそれをどのように行うかを気にすることなく扱うことができる方法です。
たとえば、車、トラック、スケートボード、飛行機など、さまざまな種類の車両が運転しているゲームがあるとします。すべて停止できますが、各車両の停止方法は異なります。いくつかの車両はギアをシフトダウンする必要があるかもしれません、そしていくつかは冷たい停止に来ることができるかもしれません。ポリモフィズムはこれを可能にします
foreach (Vehicle v in Game.Vehicles)
{
v.Stop();
}
ストップの実装方法は異なるビークルに委ねられているため、プログラムはそれを気にする必要はありません。
オブジェクトが同じメッセージにさまざまな方法で応答する必要がある機能です。
たとえば、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#などの言語。オブジェクトをオブジェクト参照に割り当てるには、インターフェイスを実装するか、共通クラスのサブクラスにすることにより、同じ型階層を共有する必要があります。
簡単..そしてシンプル。
ポリモーフィズムは、オブジェクト指向プログラミングの中で最も重要で適切な機能です。