web-dev-qa-db-ja.com

なぜ継承と多態性がそれほど広く使用されているのですか?

関数型プログラミングなどのさまざまなプログラミングパラダイムについて学ぶほど、OOP継承やポリモーフィズムなどの概念)の知恵に疑問を持ち始めます。まず、学校での継承とポリモーフィズムについて学び、当時、ポリモーフィズムは、簡単に拡張できる汎用コードを書くための素晴らしい方法のように見えました。

しかし、アヒルのタイピング(動的と静的の両方)と高階関数などの機能的機能に直面して、私は継承と多態性を、オブジェクト間の壊れやすい関係に基づいて不要な制限を課すものとして検討し始めました。ポリモーフィズムの背後にある一般的な考え方は、一度関数を記述し、後で元の関数を変更せずに新しい機能をプログラムに追加できるということです。必要なのは、必要なメソッドを実装する別の派生クラスを作成することだけです。

しかし、これは、Pythonのような動的言語であっても、C++のような静的言語であっても、アヒル型付けを介して達成する方がはるかに簡単です。

例として、以下のPython関数と、それに続く同等の静的C++関数を考えます。

def foo(obj):
   obj.doSomething()

template <class Obj>
void foo(Obj& obj)
{
   obj.doSomething();
}

OOP同等のものは、次のようなものになりますJavaコード:

public void foo(DoSomethingable obj)
{
  obj.doSomething();
}

もちろん、大きな違いは、Javaバージョンが機能する前に、インターフェースまたは継承階層を作成する必要があるということです。Javaバージョンには、作業が増え、柔軟性が低下します。さらに、ほとんどの現実世界の継承階層がやや不安定であることがわかります。私たちはすべて、形や動物の不自然な例を見てきましたが、現実の世界では、ビジネス要件の変化や新機能がさらに、サブクラス間の「is-a」関係を実際に拡張する必要がある前に作業を完了することは困難です。または、新しい要件に対応するために、階層をリモデリング/リファクタリングしてさらに基本クラスまたはインターフェースを含めることは困難です。アヒルのタイピングでは、モデリングについて心配する必要はありません。必要な機能について心配するだけです。

しかし、継承とポリモーフィズムは非常に人気があり、拡張性とコードの再利用のための主要な戦略と言っても過言ではないでしょう。では、なぜ継承と多態性がこれほどまでに成功しているのでしょうか。継承/ポリモーフィズムがアヒルのタイピングよりも優れているいくつかの重大な利点を見落としているのですか?

18
Channel72

私はほとんどあなたに同意しますが、楽しみのために悪魔の擁護者を演じます。明示的なインターフェイスは、型が何をすることになっているのかを伝える明示的、正式指定されたコントラクトを探すための単一の場所を提供します。これは、プロジェクトの唯一の開発者ではない場合に重要になります。

さらに、これらの明示的なインターフェイスは、ダックタイピングよりも効率的に実装できます。仮想関数呼び出しは、インライン化できないことを除いて、通常の関数呼び出しよりもオーバーヘッドがほとんどありません。アヒルのタイピングにはかなりのオーバーヘッドがあります。 C++スタイルの構造型付け(テンプレートを使用)は、大量のオブジェクトファイル膨張を​​生成する可能性があり(各インスタンス化はオブジェクトファイルレベルで独立しているため)、コンパイル時ではなく実行時に多態性が必要な場合は機能しません。

結論:Javaスタイルの継承とポリモーフィズムはPITAになる可能性があり、代替手段をより頻繁に使用する必要がありますが、それでも利点はあります。

22
dsimcha

継承とポリモーフィズムは広く使用されています機能するためは、特定の種類のプログラミング問題に使用されます。

学校で広く教えられているわけではありません。逆です。人々(市場)が古いツールよりもうまく機能していることを知り、学校が教え始めたため、学校で広く教えられています。 [付属書:私が最初にOOPを学んでいたとき、OOP言語を教えた大学を見つけるのは非常に困難でした。10年後、=を教えていない大学を見つけるのは困難でしたOOP言語。]

あなたが言った:

ポリモーフィズムの背後にある一般的な考え方は、一度関数を記述し、後で元の関数を変更せずに新しい機能をプログラムに追加できることです。必要なのは、必要なメソッドを実装する別の派生クラスを作成することだけです。

私は言う:

いいえ、そうではありません

あなたが説明するのはポリモーフィズムではなく、継承です。 OOP問題があります!;-)

ステップをバックアップします。ポリモーフィズムはメッセージパッシングの利点です。それは単に、各オブジェクトが独自の方法でメッセージに自由に応答できることを意味します。

だから... ダックタイピングis(またはむしろ、有効にする)ポリモーフィズム

あなたの質問の要点は、あなたが理解していないOOPまたはあなたがそれを嫌っているということではありませんが、あなたはインターフェースを定義するのが好きではありませんです。それで結構です、そして注意深い限り問題なく動作しますが、欠点は、たとえばメソッドを省略して間違えた場合、実行時までわからないことです。

これは静的対動的なものであり、LISPと同じくらい古くからの議論であり、OOPに限定されません。

29
Steven A. Lowe

私は、継承とポリモーフィズムを、オブジェクト間の壊れやすい関係のセットに基づいて不要な制限を課すものと見なし始めました。

どうして?

継承(ダックタイピングの有無にかかわらず)は、共通機能の再利用を保証します。それが一般的であれば、サブクラスで一貫して再利用されることを保証できます。

それで全部です。 「不要な制限」はありません。それは単純化です。

同様に、多態性は「ダックタイピング」が意味するものです。同じ方法。インターフェースは同じであるが、実装が異なる多くのクラス。

制限ではありません。それは単純化です。

8
S.Lott

継承は悪用されますが、アヒルのタイピングも同様です。どちらも問題を引き起こす可能性があります。

強い型付けを使用すると、コンパイル時に多くの「単体テスト」が行われます。アヒルのタイピングでは、それらを頻繁に記述する必要があります。

7
ElGringoGrande

学校で物事を学ぶことの良いところは、あなたがdoそれらを学ぶことです。それほど良くないことは、それらがいつ有用であるか、そしていつ有用でないかを理解せずに、少し独断的に受け入れるかもしれないということです。

次に、独断的にそれを学んだ場合は、後で逆説的に独断的に「反逆」することができます。それも良くありません。

そのようなアイデアと同様に、実用的なアプローチを取るのが最善です。それらが適合する場所と適合しない場所の理解を深めます。そして、彼らが売られ過ぎた方法をすべて無視します。

2
Mike Dunlavey

はい、静的型付けとインターフェースは制限です。しかし、構造化プログラミングが発明されてからのすべて(つまり、「gotoは有害と見なされる」)は、私たちを拘束することについてでした。ボブおじさんはこれについて 彼のビデオブログ で素晴らしい見解を持っています。

さて、収縮は悪いと主張することができますが、その一方で、それは他の非常に複雑なトピックに秩序、統制および親しみやすさをもたらします。

動的タイピングと直接メモリアクセスを(再)導入することで制約を緩和する(== --- ==)is非常に強力な概念ですが、多くのプログラマが対処することも難しくなります。と。特にプログラマーは、ほとんどの作業をコンパイラーとタイプセーフに頼っていました。

2
Martin Wickman

継承は、2つのクラス間の非常に強い関係です。 Javaの方が強力だとは思いません。したがって、それを意味する場合にのみ使用してください。パブリック継承は「is-a」関係であり、「通常はis-a」関係ではありません。継承を使いすぎて混乱するのは本当に簡単です。多くの場合、継承は "has-a"または "takes-functionality-from-a"を表すために使用され、通常はコンポジションによって行われます。

ポリモーフィズムは「is-a」関係の直接的な結果です。 DerivedがBaseから継承する場合、すべてのDerivedは「is-a」ベースであるため、Baseを使用する場所ならどこでもDerivedを使用できます。これが継承階層で意味をなさない場合は、階層が間違っており、継承が多すぎる可能性があります。

ダックタイピングはきちんとした機能ですが、悪用してもコンパイラーは警告を出しません。実行時に例外を処理する必要がない場合は、毎回正しい結果が得られることを確認する必要があります。静的な継承階層を定義する方が簡単な場合があります。

私は静的型付けの真のファンではありませんが(多くの場合、これは時期尚早の最適化の一種であると考えています)、一部のエラークラスを排除します。多くの人々は、これらのクラスを排除する価値があると考えています。

静的型付けと定義された継承階層よりも動的型付けとダック型付けが好きなら、それで問題ありません。ただし、Javaの方法には利点があります。

1
David Thornley

C#のクロージャーを使用すればするほど、従来のOOPを実行する回数が減ることに気づきました。継承は、実装を簡単に共有するための唯一の方法でした。そのため、継承が頻繁に使用され、概念の境界が大きくなりすぎたと思います。

canは通常、継承で行うほとんどのことを行うためにクロージャーを使用しますが、醜くなることもあります。

基本的に、それは職務に適したツールの状況です。従来のOOPは、それに適したモデルがある場合に非常にうまく機能し、クロージャーはそうでない場合に非常によく機能します。

0
Ben Hughes