私はデザインパターンについて調べていましたが、プロトタイプのデザインパターンでは、過度のサブクラス化をなくすことができました。
サブクラス化が悪いのはなぜですか?プロトタイプを使用すると、サブクラス化に対してどのような利点がありますか?
私はデザインパターンについて調べていましたが、プロトタイプのデザインパターンでは、過度のサブクラス化をなくすことができました。
サブクラス化が悪いのはなぜですか?プロトタイプを使用すると、サブクラス化に対してどのような利点がありますか?
回答:
サブクラス化があまりにも悪いのはなぜですか
「多すぎる」は判断の呼びかけです。でもそれを私から奪ってそれは悪いです。私が使用するコードにはDEEP継承があり、コードの読み取り、理解、追跡、トレース、デバッグなどが非常に難しくなります。このようなもののテストコードを書くことは事実上不可能です。
プロトタイプパターンはサブクラス化を廃止します
または、質問は「サブクラス化が多すぎないか」という意味ですか。このパターンでは、少なくともその時点で、サブテンプレート化を回避するために「テンプレート」オブジェクトのクローンを作成する必要があります。「テンプレート」をサブクラスにすることはできないというルールはありません。
継承よりも構成を優先する
このアイデアには、委任と集約も含まれます。このヒューリスティックに従うと、ソフトウェアはより柔軟になり、保守、拡張、再利用が容易になる傾向があります。
クラスがパーツで構成されている場合、実行時にそれらのパーツを置き換えることができます。これは、テスト容易性に大きな影響を与えます。
テストが簡単です。偽のパーツ(「モック」、「ダブル」、その他のテストトーク)を使用できます。コードの深い継承は、階層全体をインスタンス化して、コードの任意の部分をテストする必要があることを意味します。私たちの場合、実際の環境でコードを実行しないとそれは不可能です。たとえば、ビジネスオブジェクトをインスタンス化するには、データベースが必要です。
変更には副作用と不確実性が伴います -クラスの「基本」が多いほど、良いか悪いかにかかわらず、効果がより広範囲に広がります。副作用の不確実性のために、敢えて行わない望ましい変更があるかもしれません。または、私たちの継承チェーンのある場所にとって良い変更は、別の場所にとって悪いことです。これは確かに私の経験です。
2つの理由。
実行時のパフォーマンスです。スーパークラスからサブクラスを呼び出すたびにメソッド呼び出しを行っているので、5つのクラスをネストし、基本クラスのメソッドを呼び出す場合は、5つのメソッド呼び出しを行うことになります。ほとんどのコンパイラ/ランタイムは、これを認識して余分な呼び出しを最適化しようとしますが、非常に単純な場合にのみこれを行うのが安全です。
仲間のプログラマーの正気です。5つのクラスをネストする場合、4つの親クラスすべてを調べて、基本クラスのメソッドを実際に呼び出していることを確認する必要があります。また、プログラムをデバッグするときに、5つのメソッド呼び出しをステップ実行する必要がある場合もあります。
「多すぎる」は判断の呼びかけです。
プログラミングにおけるほとんどのことと同様に、サブクラス化は、それが理にかなっている場合、本質的に悪いわけではありません。問題解決のモデルが、パーツの構成ではなく階層としてより意味をなす場合、サブクラス化が推奨されるオプションとなる場合があります。
とはいえ、継承よりも構成を優先することは、大まかな経験則です。
サブクラス化すると、テストが難しくなる可能性があります(radarbobが指摘したように)。深い階層では、サブクラスのインスタンス化にはスーパークラス階層全体と一連の追加コードを組み込む必要があるため、問題のあるコードを分離するのはより困難です。構成アプローチを使用すると、ユニットテスト、モックオブジェクトなどを使用して、集約オブジェクトの各コンポーネントを分離およびテストできます。
サブクラス化により、実装の詳細を公開したくない場合もあります。これは、それが困難なあなたのデータの基礎となる表現へのアクセスを制御することを可能にすると、それはあなたのバッキングモデルの特定の実装にあなたを結び付けます。
私が考えることができる1つの例は、Hashtableから継承するjava.util.Propertiesです。Propertiesクラスは、文字列と文字列のペアを格納することのみを目的としています(これはJavadocに記載されています)。継承のため、HashtableのputおよびputAllメソッドは、プロパティのユーザーに公開されています。プロパティを格納する「正しい」方法はsetProperty()を使用することですが、ユーザーがput()を呼び出して文字列とオブジェクトを渡すことを妨げるものはまったくありません。
基本的に、継承のため、プロパティのセマンティクスは十分に定義されていません。さらに、プロパティのバッキングオブジェクトを変更したい場合は、プロパティがより構成的なアプローチをとった場合よりも、プロパティを使用するコードにはるかに大きな影響があります。
継承によってカプセル化が解除される場合があり、それが発生した場合、それは悪いことです。詳細はこちら。
継承のない古き良き時代には、ライブラリはそれを使用するAPIとアプリケーションを公開し、公開されているものを利用します。そして、ライブラリは、アプリを壊すことなく変化し続ける内部のギアを処理する独自の方法を持っています。
ニーズが進化するにつれて、ライブラリは進化し、より多くの顧客を持つことができます。または、独自のクラスから他のフレーバーを継承することにより、拡張機能を提供できます。ここまでは順調ですね。
ただし、基本的なことは、アプリケーションがクラスを取り上げてサブクラス化を開始すると、サブクラスは実際には内部パートナーではなくクライアントになります。ただし、パブリックAPIを定義する明確で明確な方法とは異なり、サブクラスはライブラリ内ではるかに深くなっています(すべてのプライベート変数へのアクセスなど)。それはまだ悪くはありませんが、厄介なキャラクターがライブラリ内だけでなく、アプリ内にも多すぎるAPIをブリッジする可能性があります-
本質的にこれは常にそうであるとは限らないかもしれませんが、
継承を誤用してカプセル化を解除するのは簡単です
その場合、サブクラス化の許可は間違っている可能性があります。
簡単な回答:
それはあなたが何をしているかに依存します。
拡張ボーリング回答:
開発者はアプリを作成できます。サブクラス化または(プロトタイプ)テンプレートのいずれかを使用します。時には、1つのテクニックがうまく機能します。時々他の技術はよりよく働きます。
「複数の継承」シナリオが存在するかどうかを検討するには、テクニックを選択することが最も効果的であり、また必要です。プログラミング言語が単一の継承、テンプレート、またはプロトタイプのみをサポートしている場合でも。
補足事項:
「多重継承」に関する答えがいくつかあります。主要な問題ではなく、代替品は主題に関連しています。「複数の継承と構成」についての似たような投稿がいくつかあります。両方を混ぜるケースがありました。