私はこのウィキを安定した抽象化の原則(SAP)で読んでいました。
SAPは、パッケージの安定性が高いほど、抽象的である必要があると述べています。これは、パッケージの安定性が低い(変更される可能性が高い)場合、より具体的であることを意味します。私が本当に理解していないのは、これが事実であるべき理由です。確かにすべての場合において、安定性に関係なく、抽象化に依存し、具体的な実装を隠す必要がありますか?
私はこのウィキを安定した抽象化の原則(SAP)で読んでいました。
SAPは、パッケージの安定性が高いほど、抽象的である必要があると述べています。これは、パッケージの安定性が低い(変更される可能性が高い)場合、より具体的であることを意味します。私が本当に理解していないのは、これが事実であるべき理由です。確かにすべての場合において、安定性に関係なく、抽象化に依存し、具体的な実装を隠す必要がありますか?
回答:
パッケージをAPIと考えてください。紙から例をとるために、Reader
with string Reader.Read()
とWriter
withの定義をvoid Writer.Write(string)
抽象APIとして取ります。
次にCopy
、メソッドCopier.Copy(Reader, Writer)
と実装Writer.Write(Reader.Read())
、そしておそらくいくつかの健全性チェックを含むクラスを作成できます。
さて、あなたは具体的な実装を行い、例えばFileReader
、FileWriter
、KeyboardReader
とDownloadThingsFromTheInternetReader
。
の実装を変更したい場合はどうなりますFileReader
か?問題ありません。クラスを変更して再コンパイルしてください。
抽象化の定義を変更したい場合はどうなりますReader
か?おっと、あなたはちょうどそれを変更することはできませんが、また、変更する必要がありますCopier
、FileReader
、 KeyboardReader
とDownloadThingsFromTheInternetReader
。
これは、安定した抽象化の原理の背後にある理論的根拠です。具体化を抽象化よりも不安定にします。
YAGNIのためです。
現在、1つのものの実装が1つしかない場合、なぜ余分で役に立たないレイヤーに悩まされるのでしょうか。それは不必要な複雑さにつながるだけです。さらに悪いことに、2番目の実装が来る日まで抽象化思考を提供することがあります...そしてこの日は決して起こりません。なんて無駄な仕事だ!
また、自分自身に問うべき本当の質問は、「抽象化に依存する必要があるか」ではないと思います。むしろ「モジュール性が必要ですか?」また、モジュール性は必ずしも必要ではありません。以下を参照してください。
私が働いている会社では、開発しているソフトウェアのいくつかは、通信する必要のあるハードウェアデバイスに強く結びついています。これらのデバイスは、非常に具体的な目標を達成するために開発されており、モジュール式以外のすべてのものです。最初の生成デバイスは、工場から出て行くとどこかをインストールしたら:-)、そのファームウェアとハードウェアの両方が、変更することはできません今まで。
したがって、ソフトウェアの一部が決して進化しないことは確かです。これらの部分は1つしか存在しないため、抽象化に依存する必要はなく、この実装は変更されません。コードのこれらの部分の抽象化を宣言すると、誰もが混乱し、より多くの時間がかかります(値を生成しない)。
Robert Martinが選んだ「stable」という言葉に混乱していると思います。ここで混乱が始まると思います:
これは、パッケージの安定性が低い(変更される可能性が高い)場合、より具体的であることを意味します。
元の記事に目を通すと、次のように表示されます(私の強調):
安定性という言葉の古典的な定義は、「簡単に動かない」です。 これは、この記事で使用する定義です。つまり、安定性はモジュールが変更される可能性の尺度ではありません。むしろ、それはモジュールの変更の難しさの尺度です。
明らかに、変更が難しいモジュールほど揮発性が低くなります。モジュールの変更が難しい、つまりモジュールが安定しているほど、揮発性は低くなります。
私は(著者のように)安定性の「可能性」の側面、つまり変化する可能性を考えがちなので、執筆者が「安定」という単語を選択することに常に苦労してきました。難易度は、そのモジュールを変更すると、他のモジュールの多くを破るだろうことを意味し、コードを修正するために多くの仕事になるだろう。
マーティンはまた、私にもっと多くの意味を伝える独立した、責任あるという言葉を使用しています。彼のトレーニングセミナーでは、成長する子供たちの両親と、子供たちが両親に依存しているため、彼らがどのように「責任がある」べきかというメタファーを使用しました。離婚、失業、投獄などは、親の変化が子供に及ぼす悪影響の実例です。したがって、親は子供のために「安定」している必要があります。ちなみに、この子供/親のメタファーは、必ずしもOOPの継承とは関係ありません。
それで、「責任ある」の精神に従って、私は変更するのが難しい(または変更すべきではない)ための代替の意味を思いつきました:
したがって、これらの定義をステートメントに組み込む
パッケージがより安定しているほど、それはより抽象的であるべきです
Stable Abstractions Principle(SAP)を引用して、紛らわしい用語であるstable / unstableを強調します。
最大限に安定しているパッケージは、最大限に抽象的でなければなりません。不安定なパッケージは具体的でなければなりません。パッケージの抽象度は、その安定性に比例する必要があります。
これらの紛らわしい言葉なしでそれを明確にする:
システムの他の部分に最大限に守られるパッケージは、最大限に抽象的でなければなりません。問題なく変更できるパッケージは具体的でなければなりません。パッケージの抽象度は、変更がどれほど難しいかに比例する必要があります。
あなたの質問のタイトルは尋ねます:
抽象化に依存することに重大な欠点はありますか?
抽象化を適切に作成すると(たとえば、多くのコードがそれらに依存しているために抽象化が存在する場合)、重大な欠点はないと思います。
これは、パッケージの安定性が低い(変更される可能性が高い)場合、より具体的であることを意味します。私が本当に理解していないのは、これが事実であるべき理由です。
抽象化は、すべてがそれらに依存しているため、ソフトウェアで変更するのが難しいものです。パッケージが頻繁に変更され、それが抽象化を提供する場合、それに依存する人々は、何かを変更するときに大量のコードを書き直すことを強いられます。しかし、不安定なパッケージが具体的な実装をいくつか提供している場合、変更後にはるかに少ないコードを書き直す必要があります。
したがって、パッケージが頻繁に変更される場合は、抽象化ではなく具象を提供する必要があります。そうでなければ...誰がそれを使うのでしょうか?;)
マーティンの安定性メトリックと彼が「安定性」によって意味することを覚えておいてください:
Instability = Ce / (Ca+Ce)
または:
Instability = Outgoing / (Incoming+Outgoing)
つまり、パッケージの依存関係がすべて発信である場合、パッケージは完全に不安定であると見なされます。他のものを使用しますが、使用するものはありません。その場合、それが具体的であることだけが理にかなっています。また、他に何も使用していないため、変更が最も簡単な種類のコードになり、そのコードが変更されても他に何も壊れることはありません。
一方、1つまたは複数のものによって使用されるパッケージで完全な「安定性」の反対のシナリオがあるが、ソフトウェアによって使用される中央パッケージのように、それ自体は何も使用しない場合、それはマーティンがこれをすべきであると言うときです概要。これは、依存関係の逆転原理であるSOLI(D)のDIP部分によっても強化されています。これは、基本的に、依存関係が低レベルと高レベルの両方のコードの抽象化に向かって流れる必要があることを示しています。
つまり、依存関係は一様に「安定性」に向かって流れる必要があり、より正確には、依存関係は、送信依存関係よりも着信依存関係が多いパッケージに向かって流れ、さらに、依存関係は抽象化に向かって流れる必要があります。その背後にある理論的根拠の要点は、抽象化によって、あるサブタイプを別のサブタイプに置き換えるための余地が与えられ、その抽象インターフェースへの着信依存関係を壊すことなく、インターフェースを実装する具象パーツにその程度の柔軟性を提供することです。
抽象化に依存することに重大な欠点はありますか?
ええと、私は実際には少なくとも私のドメインではMartinに同意していません。ここで、「変更する理由がない」のように、「安定性」の新しい定義を導入する必要があります。その場合、依存性は安定性に向かって流れるはずですが、抽象インターフェースが不安定な場合は助けになりません(マーティンのものではなく、繰り返し変更される傾向があるような「不安定」の私の定義による)。開発者が抽象化を正しく行うことができず、クライアントがソフトウェアをモデル化する抽象的試みを不完全または無効にするような方法で繰り返し考えを変える場合、依存関係を壊すような変更のカスケードからシステムを保護するための抽象化インターフェースの柔軟性の向上の恩恵を受けなくなります。 。私の個人的なケースでは、AAAゲームで見られるようなECSエンジンを見つけました。最も具体的:生データに向かっているが、そのようなデータは非常に安定している(「変更する必要がない」など)。私はしばしば、SEの決定を導く際に、結合の合計に対する遠心性の比率よりも、将来の変更が必要な確率がより有用な測定基準であることを発見しました。
したがって、私はDIPを少し変更して、これらのコンポーネントが抽象インターフェースであるか生データであるかに関係なく、「依存関係は、さらなる変更を必要とする可能性が最も低いコンポーネントに向かって流れる必要があります」と言います。私にとって重要なのは、デザインを壊すような直接的な変更が必要になる可能性です。抽象化は、何かが抽象化されてその確率を低下させる場合にのみ、この安定性のコンテキストで役立ちます。
適切なエンジニアとクライアントが前もってソフトウェアのニーズを予測し、安定した(変更されていない)抽象化を設計する多くの状況では、これらの抽象化は具体的な実装を交換するために必要なすべての余地を提供します。しかし、一部のドメインでは、抽象化が不安定で不十分になる傾向がある一方で、エンジンに必要なデータは、事前に予測して安定させることがはるかに容易になる場合があります。したがって、これらの場合、メンテナンス性の観点(システムの変更と拡張の容易さ)から、抽象化ではなくデータに依存関係を流すほうが実際にメリットがあります。ECSで最も不安定な部分(最も頻繁に変更される部分など)は、通常、システムに存在する機能(PhysicsSystem
例)、最も安定している部分(変更される可能性は低い)は、MotionComponent
すべてのシステムが使用する生データ(例:)のみで構成されるコンポーネントです。