JavaRush /Java Blog /Random-JA /現象としての遺伝
articles
レベル 15

現象としての遺伝

Random-JA グループに公開済み
実を言うと、最初はこの記事を書く予定はありませんでした。ここで議論したい問題は取るに足らないものであり、言及する価値さえないと考えました。しかし、このサイトの記事を書く過程で、私はフォーラムの 1 つで多重継承についての議論を提起しました。その結果、ほとんどの開発者が継承について非常に曖昧に理解していることが判明しました。そしてそれに応じて、彼は多くの間違いを犯します。継承は OOP の最も重要な機能の 1 つであるため (最も重要ではないにしても!)、この現象については別の記事を書くことにしました。* * * まず、オブジェクトとクラスという 2 つの概念を区別したいと思います。これらの概念は常に混乱しています。一方、それらは OOP の中心です。そして、私の意見では、それらの違いを知ることが必要です。ということで、オブジェクト。基本的には何でもあります。こちらがそのキューブです。木製、ブルー。辺の長さは5cmのオブジェです。そしてピラミッドもあります。プラスチック、赤。リブ10cm。これもオブジェです。彼らの共通点は何がありますか?サイズが異なります。形が違う。素材が違います。ただし、それらには共通点があります。まず、立方体も角錐も正多面体です。それらの。頂点の数と面の数の合計はエッジの数より 2 多くなります。さらに遠く。どちらの形状にも面、エッジ、頂点があります。どちらの図も肋骨の大きさなどに特徴があります。どちらの形状も回転できます。どちらの図形も描くことができます。最後の 2 つのプロパティはすでに動作になっています。等々。プログラミングを実践すると、異種オブジェクトよりも同種オブジェクトを操作する方がはるかに簡単であることがわかります。そして、これらの数字にはまだ共通点があるため、この共通点を何とかして強調したいという願望があります。ここでクラスの概念が登場します。それで、定義です。
クラスは、オブジェクトのグループの共通プロパティの記述子です。これらのプロパティは、オブジェクトの特性 (サイズ、重量、色など) と動作、役割などの両方になります。
コメント。「すべて」(すべてのプロパティの記述子)という言葉は話されていませんでした。つまり、どのオブジェクトも複数の異なるクラスに属することができます。 現象としての遺伝 - 1 幾何学的形状をベースにした同じ例を見てみましょう。最も一般的な記述は正多面体です。エッジのサイズ、面、頂点の数には関係ありません。わかっていることは、この図形には頂点、辺、面があり、辺の長さが等しいということだけです。 さらに遠く。説明をより具体的にすることができます。 この多面体を描きたいとします。 正多面体を描くという概念を導入してみましょう。絵を描くには何が必要ですか?特定の頂点座標に依存しない一般的な描画方法の説明。おそらくオブジェクトの色です。次に、クラス CubeTetrahedronを紹介しましょう。これらのクラスに属するオブジェクトは確かに正多面体です。唯一の違いは、頂点、エッジ、面の数が新しいクラスごとにすでに厳密に固定されていることです。さらに、特定の図形の種類がわかれば、描画方法を説明することができます。 これは、 Cube クラスまたは Tetrahedronクラスのオブジェクトは、描画された 正多面体クラスのオブジェクトでもあることを意味します。クラスには階層があります。この階層では、最も一般的な説明から最も具体的な説明に進みます。どのクラスのオブジェクトも、階層内のより一般的なクラスの説明にも適合することに注意してください。このクラス関係は 継承と呼ばれます。各子クラスは、より一般的な親のすべてのプロパティを継承し、(おそらく) 独自のプロパティの一部をこれらのプロパティに追加します。または、親クラスの一部のプロパティをオーバーライドします。ここで、Gradi Bucha のオブジェクト指向設計に関する古典的な本から引用したいと思います。
したがって、継承はクラス間の「である」階層を定義し、サブクラスが 1 つ以上のスーパークラスから継承します。これは実際、相続のリトマス試験紙です。クラス A と B が与えられた場合、A が B の種類「ではない」場合、A は B のサブクラスであってはなりません。
翻訳すると次のようになります。
したがって、継承はクラス間の「is」階層を定義し、サブクラスが 1 つ以上のスーパークラスから継承します。実際、これは継承を決定するテスト (文字通り、リトマス試験紙、私のメモ) です。クラス A と B があり、クラス A がクラス B のバリアント「ではない」場合、A は B のサブクラスであってはなりません。
ここまで読んだ人は、当惑してこめかみに指を回すでしょう。最初に思うのは、これは些細なことだということです。これは本当です。しかし、私がこれまで見てきたクレイジーな継承階層をどれだけ知っているか知っていれば! 冒頭で述べたディスカッションの中で、参加者の 1 人が非常に真剣に戦車を機関銃から継承しました。理由は単純で、戦車に機関銃があるからです。そして、これが最もよくある間違いです。継承は、あるオブジェクトを別のオブジェクトに含めることである集約と混同されます。戦車は機関銃ではなく、機関銃を備えています。このエラーのため、多くの場合、多重継承を使用する必要があります。それでは、直接 Java に移りましょう。相続に関しては何がありますか?この言語には 2 種類のクラスがあります。実装を含めることができるクラスと、実装を含めることができないクラスです。後者はインターフェイスと呼ばれますが、本質的には完全に抽象クラスです。 現象としての遺伝 - 2 したがって、この言語では、実装を含む可能性がある別のクラスからクラスを継承できます。ただし、1 つからのみです。なぜこれが行われたのか説明しましょう。重要なのは、各実装はその部分、つまり認識している変数とメソッドのみを処理できるということです。また、クラス Cを Aおよび Bから継承したとしても、クラス A から継承されたメソッド processA は、内部変数 aのみを操作できます。これは、 cおよびメソッド processCについて何も知らないのと同様に、 b についても何も知らないため ですprocessBメソッドも同様に 変数 b のみを操作できます。つまり、本質的には、継承された部分が分離されます。クラス C は確かにこれらのパーツを操作できますが、これらのパーツが継承されるのではなく、その一部として単に組み込まれている場合も同様に操作できます。ただし、ここには名前の重複という別の厄介な問題があります。メソッド processAprocessB が同じ名前、たとえば process である場合、クラス Cの processメソッドを呼び出すとどのような影響がありますか? 2 つのメソッドのうちどちらが呼び出されますか? もちろん、C++ にはこの状況に対応するコントロールがありますが、これは言語に調和をもたらすものではありません。したがって、実装の継承には利点はありませんが、欠点はあります。このため、Java でのこの実装の継承は放棄されました。ただし、開発者には、インターフェイスからの継承などの多重継承のオプションが残されました。Java の用語では、インターフェイスの実装。インターフェースとは何ですか? メソッドのセット。(現在、インターフェイスでの定数の定義については考慮していません。詳細については、 こちらを参照してください。) メソッドとは何ですか? そして、メソッドはその中核として、オブジェクトの動作を決定します。 getXXXdrawXXXcountXXXなど、ほとんどすべてのメソッドの名前にアクションが含まれていることは偶然ではありません。そしてインターフェースはメソッドの集合であるため、実際には インターフェースが 動作の 決定要因となります。インターフェイスを使用する別のオプションは、オブジェクトの役割を定義することです。 オブザーバー、リスナーなど この場合、メソッドは実際には、何らかの外部イベントに対する反応を具体化したものです。それは繰り返しになりますが、行動です。もちろん、オブジェクトはいくつかの異なる動作を持つことができます。レンダリングする必要がある場合は、レンダリングされます。彼が救われる必要があるなら、彼は救われます。まあ、など。したがって、動作を定義するクラスから継承できる機能は非常に便利です。同様に、オブジェクトはいくつかの異なる役割を持つことができます。ただし、 実装 その行動は完全に子クラスの良心に基づいています。インターフェイス (その実装) からの継承は、このクラスのオブジェクトがあれやこれやを実行できる必要があることを示します。そして、これをどのように行うかは、インターフェイスを実装する各クラスによって独立して決定されます。継承におけるエラーに戻りましょう。さまざまなシステムを開発した私の経験から、インターフェイスからの継承があれば、複数の実装継承を使用せずに任意のシステムを実装できることがわかります。したがって、C++ に存在する形式で多重継承が欠如しているという苦情に遭遇した場合、私にとって、これは設計が間違っていることの明らかな兆候です。最もよくある間違いは、すでに述べたものです。つまり、継承と集計が混同されています。場合によっては、誤った仮定が原因でこれが発生することがあります。それらの。たとえば、速度計を例にとると、速度は距離と時間を測定することによってのみ測定できると主張され、その後、速度計は定規と時計から正常に継承され、こうして定規と時計になると定義されています。継承。(スピードメーターで時間を測ってほしいという私のリクエストは、たいてい冗談で答えられました。あるいはまったく答えられませんでした。) ここでの間違いは何でしょうか?敷地内。実は、スピードメーターは時間を計測するものではありません。ちなみに、距離も。オドメーターは、どのスピードメーターにもありますが、同じハウジング内の 2 番目のデバイスの典型的な例です。集約。速度を測定する必要はありません。完全に取り外すこともできますが、速度測定にはまったく影響しません。時にはそのような間違いが意図的に行われることもあります。これはさらに悪いことです。「はい、それが間違っていることはわかっていますが、私にとってはそのほうが都合がよいのです。」これは何を引き起こす可能性がありますか? しかし、ここで重要なのは、大砲と機関銃から戦車を継承することです。こっちの方が便利ですね。その結果、戦車は大砲と機関銃になります。次に、飛行機に 2 つの機関銃と 1 つの大砲を装備します。何が得られるでしょうか?3 つの戦車の形をした武器が吊り下げられた航空機です。理解せずに戦車を機関銃として使用する人が間違いなく存在するからです。排他的に継承階層に従います。そして、そのような階層を設計した人が間違いを犯したので、彼は絶対に正しいでしょう。
一般的に、「このほうが自分にとって便利だ」というアプローチはよくわかりません。聞き手風に書くと便利だし、基礎文法とか言う人はカズリー。もちろん誇張していますが、主な考え方は変わりません。一時的な利便性に加えて、読み書き能力というものもあります。この概念は、非常に多くの人々の経験に基づいて定義されています。実際、これは英語で「ベスト プラクティス」、つまり最善の解決策と呼ばれるものです。そして、多くの場合、より単純に見える解決策が、将来的に多くの問題を引き起こすことになります。
もちろん、この例は非常に誇張されており、したがって不合理です。ただし、それほど明らかではないにもかかわらず、壊滅的な結果につながるケースもあります。オブジェクトを集約するのではなく継承することにより、開発者は誰でも親オブジェクトの機能を直接使用できるようになります。それはすべてを意味します。データベースを操作するクラス DBManager があると想像してください。 DBManager - DataManagerを使用して、データを操作する別のクラスを作成します。このクラスは、データ制御、変換、追加のアクションなどを実行します。一般に、ビジネス層とベース層の間の層。DBManager から DataManager を継承すると、それを使用する人は誰でもデータベースに直接アクセスできるようになります。したがって、彼は制御や変換などを回避してあらゆるアクションを実行できるようになります。 さて、意図的に危害を加えたい人は誰もおらず、直接的なアクションが有効であると仮定しましょう。しかし!ベースが変更されたと仮定します。つまり、制御または変換の原則の一部が変更されました。データマネージャーが変更されました。ただし、以前にデータベースを直接操作していたコードは引き続き機能します。おそらく彼らは彼のことを覚えていないでしょう。結果は、それを探している人が灰色になるようなクラスのエラーになります。DataManager をバイパスしてデータベースを操作することは誰にも思いつきません。ちなみに、実生活での例です。エラーを見つけるのに非常に長い時間がかかりました。最後にもう一度言います。 継承は、「である」関係がある場合にのみ使用してください。これは継承の本質、つまり子クラスのオブジェクトを基本クラスのオブジェクトとして使用できる機能だからです。クラス間に「である」関係がない場合、継承は行わないでください。決して、いかなる状況においても。そして、それが非常に便利であるという理由だけでさらにそうです。元のソースへのリンク: http://www.skipy.ru/philosophy/inheritance.html
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION