私はGoFから "Favor Composition over Inheritance"というフレーズを聞き続けます。これは、有効な包括的ステートメントであると私の友人が繰り返し不快に言及しているが、継承が存在する本当の理由は単なる動作ではないと考えるのはそれほど賢明ではありません。再利用しますが、同じ祖先から継承するクラスの階層を引数として関数に渡す必要がある場合、暗黙的に多態性を実装し、これらの子孫クラスには、祖先クラスの関数の多態性であるメソッドがあります。この方法で継承を使用することが、Microsoftの(ビジュアル)C#を使用したWPFでUIを記述するのが楽しいと思うプログラマの群れや群れの唯一の理由です。構成が冗長すぎずに、UIフレームワークのデザインの画像にどのようにうまく適合しますか?
私は互換性の衝突が継承の使用から発生する可能性があることを知っています、これはステートメントがそれを保護することを目的とするものの1つですが、プログラマー側の悪いデザインの問題ではありません(いつ継承するか、いつ作成するかを見分けるのに失敗します)パラダイムや言語ではなく?
PS:親と子の使用を通じて継承の深さを暗示することを避けるために、祖先と子孫という用語を使用しました。
かどうかが決定されたかのように、理由を尋ねます。あなたの仮定を考えてみましょう:
OOP支持者が継承を一般的に悪いことと見なしているのはなぜですか
質問の前提に同意しません。継承は一般的に悪いとは見なされておらず、誤用と過剰使用と見なされています。
GoF Design Patternsは、それが悪いことについてはそのようなことを言っていません。
GoFデザインパターンが実際に言うことを見てみましょう...
Favor composition over inheritance
の説明では、その継承とオブジェクト構成はこのように連動しますです。等々。さて、私が引用できる継承には多くの欠点もありますが、要点は、GoFは継承が悪いと主張しておらず、さまざまな手法の長所と短所を明確に教えているということです。この文脈でのfavour
はどういう意味ですか?継承を最初の選択にしないでください。バールを最初の選択にしないでください。最初にキーを試してください。
継承に関しては、クラスの継承とインターフェイスの継承(サブタイプ)の2つの主要なタイプがあります。どちらも特定のパターンに適していますが、後者はGoFが公開されて以来好まれています。あなたがOOP 90年代前半に戻って学習している場合は、BoochメソッドとRumbaughメソッドについて知っているでしょうが、今日ではほとんど耳にしません。それでも、彼らはC++(およびJava)の世代を教えていました。プログラマーは継承とオーバーライドを行いますが、最先端の技術は進んでいます。私たちは、経験から、疎結合はカプセル化を使用するだけではなく、インターフェースと依存性注入を使用することで実際に見つかることを発見しました。
継承の問題は、あまりに多くの "OO"プログラマーが問題を考えているfirstが潜在的な継承ソリューションであると考えています。継承は、人々が早い段階ですぐに理解する具体的な概念です。そのため、多くの人がそれに気づき、システムモデラーとしてこれ以上成長することはありません。システム内のすべてが継承階層に組み込まれ、コードがクラス継承によって再利用されると、いくつかの悲惨な結果がもたらされ、OOの利点は、実際に階層自体と格闘することによって補われます。実際にサブシステムインターフェイスを維持するよりも、巨大な階層を格闘することに多くの時間を費やした多くのプロジェクトを見てきましたが、私自身にも責任がありました。これは、継承の誤用の兆候です。
それはさておき、継承は多くの良いことを成し遂げるために非常に貴重です。ポリモーフィズム(特にサブタイプのポリモーフィズム)は非常に強力なツールであり、ビジターパターンなどのパターンでは、シングルディスパッチとダブルディスパッチが重要です。インターフェイスの継承は、実装ではなく、インターフェイスを設計するための鍵です。抽象クラスまたはインターフェースは、継承でのみ役立ちます。
「継承よりも優先する構成」が継承を完全に回避するためのマントラであるとあなたの友人が考えている場合、彼は誤解され、完全なツールセットの概念を理解していません。
彼が実際にGoFを読んでいないように聞こえます。彼がそれを読むと主張した場合、それはまるで彼がそれを誤解しているかのように聞こえ、もう一度読むべきです。
...それは有効な包括的な声明であると思っている私の友人ですが、それはより賢明ではありません...
はい、なぜ物事が彼らのやり方であるかを盲目的に理解するよりも理解することは常により賢明です(時間の許す限り)。
継承が存在する本当の理由は、動作の再利用だけでなく、同じ祖先から継承するクラスの階層を引数として関数に渡す必要がある場合です。
正しくありません。これは言語の実装の詳細であり、かなり重要なものですが、焦点を当てるべきではありません。
暗黙的にポリモーフィズムを実装するには、これらの子孫クラスには、祖先クラスの関数のポリモーフィズムであるメソッドがあります。
そして、それもまったく正しくありません。
それは、パラダイムや言語ではなく、プログラマー側の悪いデザイン(いつ継承するか、いつ作成するかを見分けられない)の問題ではないのですか?
それは完全にプログラマー側の悪いデザインの問題です。継承の一般的な軽蔑は、継承の概念ではなく、痛みを引き起こすコードで継承が使用されているためです。
継承(およびすべてのサブタイプ)は、一部のスーパータイプのspecializationであるためです。それだけでなく、よく行われた継承は、スーパータイプによって定義されたcontractsの特殊化です(それ以外の場合はRectangle
/Square
が実際に機能します)。プログラマーがそうすることになっていることを知っているときでさえ(そしてごく少数の人はそうします)。コントラクトの他の部分を壊さずにタイプのコントラクトを洗練することは困難です(例については、Width
/Height
のRectangle
/Square
動作を参照してください) )。さらに悪いことに、wantが実際にその限定された特殊化を行うことは、あまり役に立たないため、それほど一般的ではありません。
したがって、代わりに、ポリモーフィズムを実行するため、またはXY
andZ
を使用するために、継承を誤用するプログラマーの膨大な量があります。 (余分なフィールドを攻撃することは、脆弱であり、しばしば危険な継承の使用です)、またはちょっと特殊化する(はい、Penguin
はaBird
、しかしそれは.Fly()
メソッドが機能しない...)、または...
したがって、継承は一般的に悪い結果をもたらすため、一般的に悪いものと見なされます。