web-dev-qa-db-ja.com

抽象化に依存することに重大な欠点はありますか?

私は読んでいました 安定した抽象化の原理(SAP)に関するこのwiki

SAPは、パッケージが安定しているほど、抽象的なものである必要があると述べています。これは、パッケージの安定性が低い(変更される可能性が高い)場合は、より具体的であることを意味します。私が本当に理解していないのは、なぜこれが当てはまるのかということです。確かにすべての場合において、安定性に関係なく、抽象化に依存し、具体的な実装を隠す必要がありますか?

9
SteveCallender

パッケージをAPIと考えてください。紙から例をとるために、Readerstring Reader.Read()で、Writervoid Writer.Write(string)で定義します。抽象API。

次に、メソッドCopier.Copy(Reader, Writer)と実装Writer.Write(Reader.Read())を使用してクラスCopyを作成できます。

次に、具体的な実装を行います。 FileReaderFileWriterKeyboardReaderおよびDownloadThingsFromTheInternetReader

FileReaderの実装を変更したい場合はどうなりますか?問題ありません。クラスを変更して再コンパイルしてください。

抽象化の定義Readerを変更したい場合はどうしますか?おっと、これを変更するだけでなく、CopierFileReaderKeyboardReaderDownloadThingsFromTheInternetReaderも変更する必要があります。

これは、安定した抽象化の原理の背後にある理論的根拠です。具体化を抽象化よりも不安定にします。

7
Residuum

Robert Martinが選んだWord stable に混乱していると思います。ここで混乱が始まると思います:

これは、パッケージがstable(変更される可能性が高い)の場合、より具体的であることを意味します。

元の記事 に目を通すと、(私の強調)が表示されます。

Wordの安定性の古典的な定義は次のとおりです。 "簡単に移動できません。" これは、この記事で使用する定義です。つまり、安定性はモジュールが変更される可能性の尺度ではありません。むしろモジュールの変更における difficulty の尺度です

明らかに、変更が難しいモジュールほど揮発性が低くなります。モジュールの変更が難しい、つまりモジュールの安定性が高いほど、揮発性は低くなります。

筆者の[ stable ]の選択は、安定性の「可能性」の側面、つまりを考える傾向があるため、常に苦労してきました。変化する可能性は低い Difficulty は、そのモジュールを変更すると他の多くのモジュールが壊れることを意味し、コードを修正するのは大変な作業になります。

マーティンはまた、 independent および responsible という言葉を使用しています。彼のトレーニングセミナーでは、成長する子供たちの両親と、子供たちが両親に依存しているために彼らがどのように「責任がある」べきかについてのメタファーを使用しました。離婚、失業、投獄などは、親の変化が子供に及ぼす悪影響の実例です。したがって、親は子供のために「安定」している必要があります。ちなみに、この子供/親のメタファーは、必ずしもOOPでの継承とは関係ありません。

したがって、「責任ある」の精神に従って、変更が難しい(または変更すべきではない)の別の意味を思いつきました。

  • 義務-他のクラスがこのクラスに依存していることを意味するため、変更しないでください
  • Beholden-同上。
  • 制約-このクラスの義務は、変更の機能を制限します。

したがって、これらの定義をステートメントに組み込む

stableパッケージが多ければ多いほど、それはより抽象的であるべきです

  • obligatedパッケージが多いほど、より抽象的な
  • beholdenパッケージが多ければ多いほど、それはより抽象的なものになります
  • constrainedパッケージが多いほど、より抽象的な

安定した抽象化の原則(SAP)を引用して、混乱した単語の安定/不安定を強調します。

最大限にパッケージstableであるパッケージは、最大限に抽象的である必要があります。 nstableパッケージは具体的でなければなりません。パッケージの抽象度は、その安定性に比例する必要があります。

これらの混乱する言葉なしでそれを明確にする:

最大限にパッケージシステムの他の部分に依存するであるパッケージは、最大限に抽象的である必要があります。パッケージ問題なく変更できるは具体的です。パッケージの抽象度は変更がどれほど難しいかに比例する必要があります。

TL; DR

あなたの質問のタイトルは尋ねます:

抽象化に依存することに重大な欠点はありますか?

抽象化を適切に作成した場合(たとえば、多くのコードがそれらに依存しているために抽象化が存在する場合)、重大な欠点はないと思います。

6
Fuhrmanator

[〜#〜] yagni [〜#〜] のため。

現在1つのthingの実装が1つしかない場合、なぜ余分で役に立たないレイヤーに悩まされるのでしょうか。それは不必要な複雑さにつながるだけです。さらに悪いことに、2番目の実装が来る日に抽象化thinkingを提供することがあります...そしてこの日は決して起こりません。なんて無駄な仕事だ!

また、自分自身に問うべき本当の質問は、「抽象化に依存する必要があるか」ではないと思います。むしろ「モジュール性が必要ですか?」また、モジュール性は必ずしも必要ではありません。以下を参照してください。

私が働いている会社では、私が開発するソフトウェアのいくつかは、通信する必要のあるハードウェアデバイスに強く結びついています。これらのデバイスは、非常に具体的な目標を達成するために開発されており、モジュール式以外のすべてのものです。 :-)最初に作成されたデバイスが工場から出荷され、どこかにインストールされると、そのファームウェアとハ​​ードウェアの両方を変更することはできませんever

したがって、ソフトウェアの一部が決して進化しないことを確信できます。これらの部分は1つしか存在しないため、抽象化に依存する必要はなく、この実装は変更されません。コードのこれらの部分の抽象化を宣言すると、誰もが混乱し、時間がかかる(値を生成しない)だけです。

6
Spotted

マーティンの安定性メトリックと彼が「安定性」によって意味することを覚えておいてください:

Instability = Ce / (Ca+Ce)

または:

Instability = Outgoing / (Incoming+Outgoing)

つまり、すべての依存関係が外向きである場合、パッケージは完全に不安定であると見なされます。他のものを使用しますが、何も使用しません。その場合、それが具体的であることだけが理にかなっています。また、他に何も使用しないため、変更が最も簡単な種類のコードになるため、そのコードが変更されても他に何も壊れることはありません。

一方、1つまたは複数のものによって使用されるパッケージで完全な「安定性」の反対のシナリオがあるが、ソフトウェアによって使用される中央パッケージのように、それ自体は何も使用しない場合、それはマーティンがこれは概要。これは、依存関係の逆転原理であるSOLI(D)のDIP部分によっても強化されています。これは、基本的に、依存関係が低レベルコードと高レベルコードの両方の抽象化に向かって流れる必要があることを示しています。

つまり、依存関係は一様に「安定性」に向かって流れる必要があり、より正確には、依存関係は出力依存関係よりも着信依存関係を持つパッケージに向かって流れ、さらに、依存関係は抽象化に向かって流れる必要があります。その背後にある理論的根拠の要点は、抽象化により、あるサブタイプを別のサブタイプに置き換えるための余地が与えられ、その抽象インターフェースへの着信依存関係を壊すことなく、インターフェースを実装する具象パーツにその程度の柔軟性を提供することです。

抽象化に依存することに重大な欠点はありますか?

ええと、私は実際には少なくとも私のドメインではMartinに同意していません。ここで、「変更する理由がない」のように、「安定性」の新しい定義を紹介する必要があります。その場合、依存関係は安定性に向かって流れるはずですが、抽象インターフェースが不安定な場合は助けになりません(マーティンではなく、繰り返し変更される傾向があるような「不安定」の私の定義による)。開発者が抽象化を正しく行うことができず、クライアントがソフトウェアをモデル化する抽象的な試みを不完全または無効にするような方法で繰り返し考えを変える場合、依存関係を壊す変更のカスケードからシステムを保護するための抽象インターフェースの強化された柔軟性の恩恵を受けなくなります。 。私の個人的なケースでは、AAAゲームで見られるようなECSエンジンが、これまで取り組んできた中で最も安定しているエンジンの1つであり、設計ニーズの変化に対して最も柔軟であり、ECSでは依存関係のフローであることがわかりました最も具体的:生データに向かっているが、そのようなデータは非常に安定している(「変更が必要になる可能性が低い」など)。私はしばしば、SEの決定を導く際に、結合の合計に対する遠心性の比率よりも、将来の変更を必要とする何かの確率がより有用な測定基準であることを発見しました。

したがって、私はDIPを少し変更して、コンポーネントが抽象インターフェースであるか生データであるかに関係なく、「依存関係は、さらなる変更を必要とする可能性が最も低いコンポーネントに向かって流れる必要があります」と言います。私にとって重要なのは、デザインを壊すような直接的な変更が必要になる可能性です。抽象化は、何かが抽象化されてその確率を低下させる場合にのみ、この安定性のコンテキストで役立ちます。

適切なエンジニアとクライアントが前もってソフトウェアのニーズを予測し、安定した(変更されていない)抽象化を設計する多くのコンテキストでは、これらの抽象化は具体的な実装を交換するために必要なすべての余地を提供します。しかし、一部のドメインでは、抽象化が不安定で不十分になる傾向がある一方で、エンジンに必要なデータは、事前に予測して安定させることがはるかに容易になる場合があります。したがって、これらの場合、依存性を抽象化ではなくデータに流すほうが、保守性の観点(システムの変更と拡張の容易さ)の観点から実際に有益です。 ECSで最も不安定な部分(最も頻繁に変更される部分など)は通常、システムに存在する機能(PhysicsSystemなど)ですが、最も安定した部分(変更される可能性が最も低い部分)はすべてのシステムが使用する生データ(MotionComponentなど)のみで構成されるコンポーネント.

0
user204677

これは、パッケージの安定性が低い(変更される可能性が高い)場合は、より具体的であることを意味します。私が本当に理解していないのは、これが事実であるべき理由です。

抽象化は、すべてがそれらに依存しているため、ソフトウェアで変更するのが難しいものです。パッケージが頻繁に変更され、抽象化が提供されている場合、それに依存する人々は、何かを変更するときに大量のコードを書き直す必要があります。しかし、不安定なパッケージがいくつかの具体的な実装を提供している場合、変更後にはるかに少ないコードを書き直す必要があります。

したがって、パッケージが頻繁に変更される場合は、抽象化ではなく具象を提供する必要があります。そうでなければ...誰がそれを使うのでしょうか? ;)

0