OOPキー論争
ご存知のように、古典的なOOPは3つの柱に基づいています。
従来のデフォルトの実装:
- カプセル化-パブリックおよびプライベートクラスメンバー
- 継承-1つの祖先クラス、クラスの保護されたメンバーを拡張することによる機能の実装。
- 多態性-祖先クラスの仮想メソッド。
しかし、1986年に、深刻な問題が特定されました 。これは次のように簡単に定式化されます。
継承がカプセル化を破る
- 下位クラスは、先祖クラスの保護されたメンバーにアクセスできます。 その他はすべて、クラスのパブリックインターフェイスでのみ使用できます。 ハッキングの究極のケースは、
反公共の霜パターンです。 - 仮想メソッドをオーバーラップすることによってのみ、実際に祖先の動作を変更できます。
- リスコフ置換の原則では、子孫クラスが祖先クラスのすべての要件を満たす必要があります。
- 条項3に完全に準拠して条項2を満たすために、子孫クラスには、重複した仮想メソッドの呼び出し時間と実装に関する完全な情報が必要です。
- パラグラフ4の情報は、プライベートメンバーとそのコードを含む祖先クラスの実装に依存します。
理論的には、私たちはすでに昔ながらの拒否を持っていますが、実践はどうですか?
- 継承によって作成される依存関係は非常に強力です。
- 相続人は先祖の変化に対して過敏です。
- 他の誰かのコードから継承すると、付随するときに地獄の痛みが追加されます。ライブラリ開発者は、基本クラスのわずかな変更で下位互換性の破損による障害のリスクを負い、使用されるライブラリが更新されると申請者が退行します。
クラス(WinForms、WPF、WebForms、ASP.NET)からの継承を必要とするフレームワークを使用するすべての人は、経験の3つのポイントすべてについて確認を簡単に見つけることができます。
本当にそんなに悪いの?
理論的解決策
問題の影響は、特定の規則を採用することで緩和できます。
1.保護されたメンバーは必要ありません
この協定は、クラスとしての公共の霜を排除します。
2.先祖の仮想メソッドは何もしません
この合意により、先祖の実装に関する知識と、すでに子孫に実装されている実装からの独立性を組み合わせることができます。
3.祖先の仮想メソッドは、そのコードで呼び出されることはありません
この合意により、子孫は祖先の内部実装に依存せず、すべての仮想メソッドの宣伝も必要とします。
4.祖先インスタンスは作成されません。
この合意により、仮想メソッドの要件の不一致(パブリッククラスコントラクト)と、何もしない義務(保護されたクラスコントラクト)が排除されます。 リスコフ置換の原理は、祖先の閉じられた内容と悪意のある関係に入ることなく観察できます。
5.祖先には仮想メンバーがありません
以前の契約を条件として、非仮想の祖先メンバーは役に立たなくなり、清算の対象となります。
結果:祖先クラスがパブリック仮想空メソッドと子孫のそれらの要件で構成されている場合、継承はカプセル化を解除しません。 証明するために必要でした。
途中で、従来の祖先からの多重継承の場合のひし形問題を解決する機会を得ます。 しかし、これはすべての理論ですが、必要なのは...
実用的なソリューション
- 仮想ダミーメソッドはすでに多くの言語に存在し、 抽象という名誉あるタイトルを持っています。
- インスタンス化できないクラスも多くの言語で利用でき、同じタイトルを持っています。
- C ++のこれらの規則への完全な準拠は、 コンポーネントオブジェクトモデルの設計と実装のパターンとして使用されました。
- さて、最良の部分:C#および他の多くの言語では、規則は一流の「インターフェース」要素として実装されます 。
名前の由来は明らかです-規約に準拠した結果、クラスからはパブリックインターフェイスのみが残ります。 また、通常のクラスからの多重継承がまれな場合は、インターフェースから何の制限もなく利用できます。
まとめ
- クラスからの継承はないが、インターフェイスからの継承がある言語(Goなど)では、オブジェクト指向のタイトルを奪うことはできません。 さらに、このようなOOPの実装は、理論的にはより正確で、実際には安全です。
- (実装を持つ)通常のクラスからの継承は、非常に特殊で非常に危険なアーキズムです。
- どうしても必要な場合を除き、実装を継承しないでください。
- 実装を継承するように特別に設計されたクラスを除くすべてのクラスに対して、sealed修飾子(.NETの場合)または同等のものを使用します。
- 公開されていないクラスは避けてください:継承がそのアセンブリを超えない限り、利益が得られ、害に限定されます。
PS:エキストラと批判は伝統的に歓迎されています。