JavaScriptのポリモーフィズムとは何ですか?


90

ポリモーフィズムについてインターネットで見つけた可能性のある記事をいくつか読んだことがあります。でも、その意味とその重要性がよくわからなかったと思います。ほとんどの記事では、なぜそれが重要であり、OOPで(もちろんJavaScriptで)多相的な動作を実現できるかについては触れていません。

それを実装する方法がわからないので、コード例を提供することはできません。

  1. それは何ですか?
  2. なぜそれが必要なのですか?
  3. 使い方?
  4. JavaScriptでこのポリモーフィックな動作を実現するにはどうすればよいですか?

私はこの例を持っています。しかし、このコードの結果がどうなるかは簡単に理解できます。ポリモーフィズム自体についての明確な考えはありません。

function Person(age, weight) {
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo.";
    }
}
function Employee(age, weight, salary) {
    this.salary = salary;
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo " +
        "and earns " + this.salary + " dollar.";
    }
}

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
  // The argument, 'obj', can be of any kind
  // which method, getInfo(), to be executed depend on the object
  // that 'obj' refer to.

function showInfo(obj) {
    document.write(obj.getInfo() + "<br>");
}

var person = new Person(50,90);
var employee = new Employee(43,80,50000);
showInfo(person);
showInfo(employee);

この質問は多分多すぎてStackOverflowでうまく機能しません。私たちにできる最善のことは、あなたを他の多態性の説明にリンクすることです。StackOverflowは、「多態性はXYZであるとソースが言ったが、Yは何を意味しているのか」など、特定の問題についての特定の質問または説明への回答に最適です。
Vitruvius 14

2
あなたはそれを必要としません。まったく。JSにはクラスも必要ありません。実際、アプリ構築には、間違いなくもっと優れたパラダイムがたくさんあります。適用/呼び出し/バインドにより、同種性の必要がなくなり、ソフトオブジェクトを使用すると、事前に装飾したり特別なケースを継承したりすることなく、ニーズに合わせて何でも変更できます。
dandavis 14

1
ポリモーフィズムはオブジェクト指向だけに関連しているわけではなく、多くの意味があります。継承なしでポリモーフィズムが可能かという質問の下で、この別の回答を読むことをお勧めします。
Edwin Dalorzo、2014

継承は通常、JavaScriptでは正しく行われません。Childのプロトタイプとして使用されるParentのインスタンスを作成するには、コンストラクター関数とプロトタイプがオブジェクトの定義と作成で果たす役割を理解していないことがわかります。詳細については、この回答をご覧ください。stackoverflow.com
a

回答:


99

ポリモーフィズムは、オブジェクト指向プログラミング(OOP)の理念の1つです。振る舞いを共有し、共有された振る舞いを特定の振る舞いでオーバーライドできるようにオブジェクトを設計する習慣です。多態性は、これを実現するために継承を利用します。

OOPでは、すべてがオブジェクトとしてモデル化されていると見なされます。この抽象化は、自動車のナットやボルトに至るまで、または年式、型式、およびモデルを持つ単純な車のタイプまで幅広く適用できます。

ポリモーフィックな車のシナリオを作成するには、基本車のタイプがあり、次に車から継承し、車が持つ基本的な動作に加えて独自の動作を提供するサブクラスがあります。たとえば、サブクラスはTowTruckであり、年式とモデルがまだありますが、IsTowingのフラグと同じくらい基本的で、リフトの詳細と同じくらい複雑な追加の動作とプロパティもある可能性があります。

人と従業員の例に戻ると、すべての従業員は人ですが、すべての人は従業員ではありません。つまり、人々はスーパークラスになり、従業員はサブクラスになります。人には年齢と体重があるかもしれませんが、給与はありません。従業員は、本質的に年齢と体重を持っている人ですが、従業員であるため、給与も持っています。

これを容易にするために、最初にスーパークラス(Person)を書き出します。

function Person(age,weight){
 this.age = age;
 this.weight = weight;
}

そして、私たちは人に彼らの情報を共有する能力を与えます

Person.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo.";
};

次に、Person、Employeeのサブクラスを作成します

function Employee(age,weight,salary){
 this.age = age;
 this.weight = weight;
 this.salary = salary;
}
Employee.prototype = new Person();

そして、従業員により適したものを定義することにより、getInfoの動作をオーバーライドします。

Employee.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo " +
    "and earns " + this.salary + " dollar.";  
};

これらは、元のコードの使用と同様に使用できます

var person = new Person(50,90);
var employee = new Employee(43,80,50000);

console.log(person.getInfo());
console.log(employee.getInfo());

ただし、ここでは継承を使用してもあまりメリットはありません。Employeeのコンストラクタはpersonのコンストラクタと非常に似ており、プロトタイプ内の唯一の関数がオーバーライドされているためです。ポリモーフィックデザインの力は、動作を共有することです。


2
@ user3138436-正解です。プロトタイプチェーンはgetInfo、チェーン内で人のチェーンよりも高いため、最初に出現するのが従業員のものであるかどうか検査されます。それが私が「オーバーライドされた」と言ったときに私が意味したことです。
Travis J

17
コンストラクター(Person.call(this、arg))を再利用せず、EmployeeプロトタイプをPersonのインスタンスに設定していません。プロトタイプは共有メンバーが移動する場所であり、コンストラクター関数はインスタンス固有のメンバーが作成される場所です。あなたのサンプルは、コンストラクター関数の再利用とコピープロトタイプコードを使用し、プロトタイプの部分を間違って継承しています(Personには、特に変更可能なメンバーがある場合は、Employee.prototypeにビジネスがないインスタンス固有のメンバーがあります)。コンストラクター関数とプロトタイプを使用した継承の詳細については、こちらをご覧ください:stackoverflow.com/a/16063711/1641941
HMR

3
従業員が人のgetinfoを再利用して拡張するためにはreturn Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";、コピーペーストコードを再利用する代わりに、単に行うことができます。
HMR 2014

3
ここでポリモーフィズムが適用されましたか?
albert Jegani 2015

2
@rpegポリモーフィズムのポイントは、OPの例でよくわかります。関数showInfo(); 一般的なオブジェクトを受け入れます。現在、ポリモーフィズムは、オブジェクトタイプに応じて異なる反応をする機能です。この回答は、特定のオブジェクトごとにgetInfo()を呼び出すため、これでは十分に明確にならないと思います。
ステファン

26

この別の回答で説明されているように、ポリモーフィズムにはさまざまな解釈があります。

私が今まで読んだことのある主題についての最も良い説明は、有名な型理論家であるLuca Cardelliによる記事です。記事の名前は、「タイプの理解」、「データの抽象化」、「ポリモーフィズム」と呼ばれています。

それは何ですか?

Cardelliは、この記事でいくつかのタイプの多態性を定義しています。

  • ユニバーサル
    • パラメトリック
    • 包含
  • このために
    • オーバーロード
    • 強制

おそらくJavaScriptでは、静的な型システムでは古典的な型のポリモーフィズムがより明白になるのに対し、JavaScriptには動的な型システムがあるため、ポリモーフィズムの影響を確認するのは少し難しくなります。

そのため、たとえば、JavaScriptのコンパイル時にメソッドや関数のオーバーロードや自動型強制はありません。動的言語では、これらのことのほとんどを当然のことと考えています。言語の動的な性質により、JavaScriptのパラメトリックポリモーフィズムのようなものは必要ありません。

それでも、JavaScriptにはタイプ継承の形式があり、JavaやC#などの他のオブジェクト指向プログラミング言語で一般的に行われているのと同様の方法で、サブタイプのポリモーフィズム(上記のCardelliでは包含ポリモーフィズムとして分類)の同じアイデアをエミュレートします(上記で共有した別の答え)。

動的言語で非常に一般的な別の形態の多態性は、ダックタイピングと呼ばれます

多態性がオブジェクト指向プログラミングにのみ関連していると信じることは間違いです。他のプログラミングモデル(関数型、手続き型、ロジックなど)は、型システムでさまざまな形のポリモーフィズムを提供します。おそらく、OOPにしか使用されていないものとは少し不慣れな方法で提供されます。

なぜ必要なのか?

ポリモーフィズムは、ソフトウェアの多くの優れた属性を助長します。とりわけ、モジュール化と再利用性を助長し、型システムをより柔軟で柔軟にします。それがなければ、型について推論することは本当に難しいでしょう。ポリモーフィズムは、パブリックインターフェイスを満たす限り、1つのタイプを他の互換性のあるタイプで置き換えることができるため、情報の隠蔽とモジュール性も促進されます。

どのように機能しますか?

これは答えるのが簡単ではありません、それを実装する方法は言語によって異なります。JavaScriptの場合、前述のように、プロトタイプ継承を使用してタイプ階層の形で具体化し、ダックタイピングを使用してそれを利用することもできます。

件名は少し広範で、1つの投稿で2つの多くの質問を開きました。おそらくCardelliの論文を読むことから始めて、言語やプログラミングパラダイムに関係なくポリモーフィズムを理解しようとすると、理論的な概念と、JavaScriptのような特定の言語がそれらのアイデアを実装するために提供する必要のあるものとの関連付けを開始します。


14

多型の目的は何ですか?

ポリモーフィズムは、型の等価性の条件を緩和することにより、(重要な)静的型の安全性を失うことなく、静的型システムをより柔軟にします。型エラーが含まれていない場合にのみプログラムが実行されるという証拠は残っています。

ポリモーフィック関数またはデータ型は、幅広いシナリオで使用できるため、モノモーフィック関数またはデータ型よりも一般的です。この意味で、ポリモーフィズムは、厳密に型指定された言語における一般化の概念を表しています。

これはJavascriptにどのように適用されますか?

JavaScriptは、動的な型システムが弱くなっています。このような型システムは、1つの型のみを含む厳密な型システムと同等です。そのような型は巨大な共用体型(疑似構文)と考えることができます。

type T =
 | Undefined
 | Null
 | Number
 | String
 | Boolean
 | Symbol
 | Object
 | Array
 | Map
 | ...

すべての値は、実行時にこれらの代替タイプの1つに関連付けられます。また、JavaScriptの型付けは弱いため、すべての値はその型を何度でも変更できます。

型の理論的観点を取り、型が1つだけであると考えると、Javascriptの型システムにはポリモーフィズムの概念がないことが確実に言えます。代わりに、ダックタイピングと暗黙の型強制があります。

しかし、これは私たちのプログラムの型について考えることを妨げるべきではありません。JavaScriptにはタイプがないため、コーディングプロセス中にタイプを推測する必要があります。私たちの心は、不足しているコンパイラーに代わる必要があります。つまり、プログラムを見るとすぐに、アルゴリズムだけでなく、基礎となる(多態的な)型も認識する必要があります。これらのタイプは、より信頼性が高く、より堅牢なプログラムを構築するのに役立ちます。

これを適切に行うために、多型の最も一般的な症状の概要を説明します。

パラメトリックポリモーフィズム(別名ジェネリック)

パラメトリックポリモーフィズムは、型はまったく関係がないため、異なる型は交換可能であると述べています。パラメトリックポリモーフィック型の1つ以上のパラメーターを定義する関数は、対応する引数について何も知らないで、すべての型に適用できるため、それらをすべて同じように扱う必要があります。このような関数は、データの一部ではない引数のプロパティでのみ機能するため、これはかなり制限されています。

// parametric polymorphic functions

const id = x => x;

id(1); // 1
id("foo"); // "foo"

const k = x => y => x;
const k_ = x => y => y;

k(1) ("foo"); // 1
k_(1) ("foo"); // "foo"

const append = x => xs => xs.concat([x]);

append(3) ([1, 2]); // [1, 2, 3]
append("c") (["a", "b"]); // ["a", "b", "c"]

アドホックなポリモーフィズム(別名オーバーロード)

アドホックポリモーフィズムは、異なるタイプは特定の目的でのみ同等であると述べています。この意味で同等であるためには、型はその目的に固有の一連の関数を実装する必要があります。アドホックな多相型の1つ以上のパラメーターを定義する関数は、各引数に関連付けられている関数のセットを知る必要があります。

アドホックポリモーフィズムは、関数を型のより大きなドメインと互換性があるようにします。次の例は、「マップオーバー」の目的と、型がこの制約を実装する方法を示しています。関数のセットの代わりに、「マップ可能」制約には単一のmap関数のみが含まれます。

// Option type
class Option {
  cata(pattern, option) {
    return pattern[option.constructor.name](option.x);
  }
  
  map(f, opt) {
    return this.cata({Some: x => new Some(f(x)), None: () => this}, opt);
  }
};

class Some extends Option {
  constructor(x) {
    super(x);
    this.x = x;
  }
};

class None extends Option {
  constructor() {
    super();
  }
};


// ad-hoc polymorphic function
const map = f => t => t.map(f, t);

// helper/data

const sqr = x => x * x;

const xs = [1, 2, 3];
const x = new Some(5);
const y = new None();

// application

console.log(
  map(sqr) (xs) // [1, 4, 9]
);

console.log(
  map(sqr) (x) // Some {x: 25}
);

console.log(
  map(sqr) (y) // None {}
);

サブタイプ多型

他の回答はすでにサブタイプ多型をカバーしているので、私はそれをスキップします。

構造的多型(別名Strutrualサブタイピング)

構造ポリモーフィズムでは、異なるタイプは同等であり、同じ方法で同じ構造が含まれている場合、一方のタイプにはもう一方のタイプのすべてのプロパティがありますが、追加のプロパティが含まれる場合があります。そうは言っても、構造多型はコンパイル時のダックタイピングであり、確かにいくつかの追加の型安全性を提供します。しかし、いくつかのプロパティを共有しているという理由だけで2つの値が同じ型であると主張することにより、値の意味レベルは完全に無視されます。

const weight = {value: 90, foo: true};
const speed =  {value: 90, foo: false, bar: [1, 2, 3]};

残念ながら、speedはのサブタイプと見なされweightvalueプロパティを比較するとすぐに、リンゴとオレンジを事実上比較しています。


1
これは多くの点で、受け入れられた回答よりも正確です(確かに完全です)が、同じアクセシビリティはありません。この回答は、質問者がすでに賢すぎて質問に悩まされないことを前提としています:)
Jared Smith

@JaredSmith私は、主題を簡単に把握できる段落に要約しようとしました。しかし、ドリルを深くすればするほど、より複雑になります。型付けされていない言語でポリモーフィズムの良い情報源を見つけたことがないので、それでもこの答えは価値があると思います。

編集により、アクセシビリティが大幅に向上します。動的言語でのポリモーフィズムの有用性についてはたくさんありますが、JSでの良い例を考えるのに苦労しています。よりよい例は、ユーザー定義型がのような多態性関数を処理できるようにするPythonの魔法のメソッドですlen。または多分conjclojureから。
Jared Smith

8

それは何ですか?

ポリ=多、モーフィズム=形態または行動シフト。

なぜそれが必要なのですか?

プログラミングでは、関数(関数Xとしましょう)のインターフェイスを、さまざまなタイプや数のパラメーターを受け入れるのに十分な柔軟性にしたい場合に使用します。また、パラメーターのタイプまたは数値の変更に基づいて、関数Xの動作を変更したい場合があります(モーフィズム)。

使い方?

X関数の複数の実装を記述します。各実装は、異なるパラメータータイプまたはパラメーター数を受け入れます。パラメーターのタイプまたは数に基づいて、コンパイラー(実行時)は、Xがあるコードから呼び出されたときに実行するXの実装を決定します。

JavaScriptでこのポリモーフィックな動作を実現するにはどうすればよいですか?

JSは型付き言語ではないため、ポリモーフィズムのようなOOPの概念を使用することは意図されていません。ただし、新しいバージョンのJSにはクラスが含まれるようになり、多形性がJSでも理にかなっている可能性があります。他の回答はいくつかの興味深い回避策を提供します。


3

ポリモーフィズムとは、異なるオブジェクトで同じメソッドを呼び出し、各オブジェクトが異なる方法で応答する機能をPOLYMORPHISMといいます

    function Animal(sound){
    this.sound=sound;
    this.speak=function(){
    			return this.sound;
    	}
    }
//one method 
    function showInfo(obj){
    		console.log(obj.speak());
    }
//different objects
    var dog = new Animal("woof");
    var cat = new Animal("meow");
    var cow = new Animal("humbow");
//responds different ways
    showInfo(dog);
    showInfo(cat);
    showInfo(cow);


2

JavaScriptはインタプリタ言語であり、コンパイルされた言語ではありません。

コンパイル時の多態性(または静的なポリモーフィズム)

そのため、JavaScriptではメソッドのオーバーロードはできません。

ただし、動的(実行時)ポリモーフィズムは実行時に存在するポリモーフィズムであるため、JavaScriptでメソッドのオーバーライドが可能です。

別の例はPHPです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.