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


9

私はこのウィキを安定した抽象化の原則(SAP)で読んでいました

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


提供する抽象化を使用せずに、使い慣れたコンポーネントを使用してみてください。ただし、慣れているレベルよりも1レベル低いレベルですべてを詳細に実行してください。それはあなたに抽象化の利点と欠点のかなり良い印象を与えるでしょう。
Kilian Foth、2015年

2
リンクされた記事や記事の基礎となっている本を読みましか?
イェルクWミッターク

1
+1良い質問です。特に、安定性と抽象化の関係はすぐに直感的ではないと思います。 この記事の11ページ目は役立ちますが、抽象的なケースの例は理にかなっていますが、おそらく誰かが具体的なケースのより明確な例を書くことができます。保留の解除をリクエストしてください。
マイクはモニカサポート

これらの抽象化のためにどのような問題領域を扱っていますか?C2で述べたように、「顧客、従業員、請求書、部品表、製品、SKU、支払い小切手などの現実世界のドメインのモデリングでは、安定した抽象化を見つけるのは難しい場合があります。計算ドメイン-スタック、キュー、関数、ツリー、プロセス、スレッド、グラフィカルウィジェット、レポート、フォームなどの世界-安定している可能性がはるかに高い。」「一部のドメインでは、安定した抽象化を実現するのが困難です。」SAPで解決しようとしている問題を知らなければ、良い答えを出すのは困難です。

@JörgWMittagとMike-ええ、私は記事を読みました。「不安定なパッケージが具体的でなければならない」理由が説明されていないように感じます。上記の記事の13ページで、彼はグラフを示していますが、グラフの(1,1)を避けなければならない理由をあまり詳しく説明していません。基本的に不安定であるということは、依存性が低いことを意味し、抽象化を使用する必要がないためですか?もしそうなら...使用抽象化へのそれではない良い方法は、念のため、とにかく..です要件の変化と安定性の変更
SteveCallender

回答:


7

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

次にCopy、メソッドCopier.Copy(Reader, Writer)と実装Writer.Write(Reader.Read())、そしておそらくいくつかの健全性チェックを含むクラスを作成できます。

さて、あなたは具体的な実装を行い、例えばFileReaderFileWriterKeyboardReaderDownloadThingsFromTheInternetReader

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

抽象化の定義を変更したい場合はどうなりますReaderか?おっと、あなたはちょうどそれを変更することはできませんが、また、変更する必要がありますCopierFileReaderKeyboardReaderDownloadThingsFromTheInternetReader

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


1
私はあなたが言うすべてに同意しますが、著者の安定性の定義とあなたの定義は異なると思います。安定性を変更する必要があるとして扱い、著者は「安定性はモジュールが変更される可能性の尺度ではなく、モジュールの変更の難しさの尺度です」と述べています。ですから、私の質問は、簡単に変更できるパッケージが抽象的ではなく具体的​​であることがなぜ有益なのかということです。
SteveCallender 2015年

1
@SteveCallender微妙な違いです。「安定性」の作成者の定義は、ほとんどの人が「安定性が必要」と呼んでいるものです。つまり、モジュールに依存するモジュールが多いほど、モジュールはより「安定」している必要があります。
Residuum 2015年

6

YAGNIのためです。

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

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

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

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


1
私はYAGNIに同意する傾向がありますが、あなたの例については疑問に思います。あなたは繰り返すことはありません任意の異なるデバイスでコードを?同じ会社のデバイス間で共通のコードがないとは信じがたいです。また、ファームウェアのバグを修正しない場合、クライアントはどのように好むでしょうか?バグはないって言ってるの?4つの異なる実装にバグがある同じコードがある場合、共通モジュールにない場合はバグを4回修正する必要があります。
Fuhrmanator 2015

1
@Fuhrmanatorの共通コードは、抽象化とは異なります。共通のコードはヘルパーメソッドまたはライブラリを意味するだけです-抽象化は必要ありません。
Eilon、2015

@Fuhrmanatorもちろんライブラリには共通のコードがありますが、Eilonが言ったように、すべてが抽象化に依存しているわけではありません(ただし一部は依存しています)。バグがあるとは決して言いませんでしたし、パッチを当てることはできないと述べました(理由はOPの質問の範囲外です)。
2015

@Eilon私のコメントはモジュール性については必ずしも必要ではありませんでした(抽象化ではありません)。
Fuhrmanator 2015

@Spottedパッチを適用できないことについては問題ありません。これは非常に具体的な例であり、ほとんどのソフトウェアの典型ではありません。
Fuhrmanator 2015

6

Robert Martinが選んだ「stable」という言葉に混乱していると思います。ここで混乱が始まると思います:

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

元の記事に目を通すと、次のように表示されます(私の強調):

安定性という言葉の古典的な定義は、「簡単に動かない」です。 これは、この記事で使用する定義です。つまり、安定性はモジュールが変更される可能性の尺度ではありません。むしろ、それはモジュールの変更の難しさの尺度です

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

私は(著者のように)安定性の「可能性」の側面、つまり変化する可能性を考えがちなので、執筆者が「安定」という単語を選択することに常に苦労してきました。難易度は、そのモジュールを変更すると、他のモジュールの多くを破るだろうことを意味し、コードを修正するために多くの仕事になるだろう。

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

それで、「責任ある」の精神に従って、私は変更するのが難しい(または変更すべきではない)ための代替の意味を思いつきました:

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

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

パッケージがより安定しているほど、それはより抽象的であるべきです

  • パッケージの義務が多いほど、それはより抽象的であるべきです
  • パッケージをより多く見守るほど、それはより抽象的なものになります
  • より多くの制約パッケージは、より抽象的なそれはする必要があります

Stable Abstractions Principle(SAP)を引用して、紛らわしい用語であるstable / unstableを強調します。

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

これらの紛らわしい言葉なしでそれを明確にする:

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

TL; DR

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

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

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


0

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

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

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


0

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

Instability = Ce / (Ca+Ce)

または:

Instability = Outgoing / (Incoming+Outgoing)

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

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

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

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.