抽象化によって詳細を隠すことの価値は何ですか?透明性に価値はありませんか?


30

バックグラウンド

私は抽象化の大ファンではありません。インターフェースの適応性、移植性、再利用性などの恩恵を受けることができると認めます。そこには本当の利点がありますが、それを疑いたくないので無視しましょう。

抽象化のもう1つの主要な「利点」があります。これは、この抽象化のユーザーから実装ロジックと詳細を隠すことです。引数は、詳細を知る必要はなく、この時点で独自のロジックに集中する必要があるということです。理論的には理にかなっています。

ただし、大規模なエンタープライズアプリケーションを保守しているときはいつでも、常に詳細を知る必要があります。それは、何かが正確に何であるかを正確に見つけるために、毎回、抽象化を深く掘り下げる大きな手間となります。つまり、使用されているストアドプロシージャを見つける前に約12回「宣言を開く」必要があります。

この「詳細を隠す」という考え方は、邪魔をしているようです。私は常に、より透明なインターフェースとより少ない抽象化を望んでいます。私は高レベルのソースコードを読むことができ、それが何をするのかを知ることができますが、それがどのように行われるのか、それがどのように行われるのかを知る必要はありません。

何が起きてる?私がこれまでに取り組んできたすべてのシステムは、(少なくともこの観点から)ひどく設計されていますか?

私の哲学

ソフトウェアを開発するとき、私はArchLinuxの哲学と密接に関連していると思う哲学に従うように感じています:

Arch Linuxは、GNU / Linuxシステムに固有の複雑さを保持しながら、それらを適切に組織化して透明性を保ちます。Arch Linuxの開発者とユーザーは、システムの複雑さを隠そうとすると、実際にはさらに複雑なシステムになるため、避けるべきだと考えています。

したがって、抽象レイヤーの背後にソフトウェアの複雑さを隠そうとはしません。私は抽象化を悪用しようとしますが、それの奴隷になることはしません。

心からの質問

  1. 詳細を隠すことには本当の価値がありますか?
  2. 透明性を犠牲にしませんか?
  3. この透明性は重要ではありませんか?

7
抽象化は、悪いデザインという形で悪用される可能性があります。しかし、それは原則として抽象化が価値がないという意味ではありません。
バーナード

4
そこには良い質問があると思いますが、それは抽象化に対する暴言によく似ています。あなたはそれを軽視し、あなたの実際の質問をもっと引き出してくれるかもしれません。
PersonalNexus

4
「詳細を隠す」という正しい定義を使用していると確信していますか?この文脈では、何かの内部の仕組みを学ぶことを妨げることではなく、結合を減らすことです。
アンドレスF.

24
デスクで電圧計とオシロスコープを使用してプログラミングするのが好きでない限り、抽象化の上にある抽象化の上にある抽象化に対してのみプログラミングしています。実際にビットではなく電圧を操作しているという詳細を隠すことに価値はありますか?そうすると透明性が犠牲になりますか?その透明性は貴重ですか?
エリックリッパー

8
あなたが問題を抱えているのは抽象化ではなく、実際には何も抽象化しない空の間接層です。はい、それらは大規模なエンタープライズシステムでよく見られますが、よくありません。
マイケルボルグ

回答:


47

詳細を非表示にする理由は、詳細を非表示にするためではありません。依存コードを壊すことなく実装を変更できるようにすることです。

オブジェクトのリストがあり、各オブジェクトにはNameプロパティと他のデータがあると想像してください。そして、多くの場合、リスト内でName特定の文字列に一致するアイテムを見つける必要があります。

明白な方法は、各項目を1つずつループしName、文字列と一致するかどうかを確認することです。ただし、リストに数千個のアイテムがある場合のように、時間がかかりすぎることがわかった場合は、文字列オブジェクト辞書の検索に置き換えることができます。

これで、リストを取得してループすることですべてのルックアップが完了した場合、これを修正するために膨大な作業が必要になります。あなたが図書館にいて、サードパーティのユーザーがそれを使用している場合はさらに困難です。外出しコードを修正することはできません!

しかしFindByName、名前検索のプロセスをカプセル化するメソッドがあれば、それを実装する方法を変更するだけで、それを呼び出すすべてのコードが動作し続け、無料ではるかに高速になります。それが抽象化とカプセル化の真の価値です。


私はこれに同意しますが、これは問題のポイントではありませんでした。特定の機能をカプセル化する方法が有用であることは完全に同意できますが、これが必ずしも動機ではないように感じます。私が間違っている?
user606723

3
@ User606723:あるはずです。それはそれは常に意味するものではありませんです。一部の人々はポイントを理解せず、より多くのレイヤーをより多くのレイヤーの上に積み重ねて、物事を混乱に変えます。これが、経験豊富なプログラマーが新しい開発者に新しい技術やテクニックを理解するまで採用しないように勧める理由です。
メイソンウィーラー

3
@ user606723:透明性は密結合を促進するため、常に悪いとは限りませんが、通常はそうです。
マルフィスト

3
メイソンが説明する問題は、カーゴカルトプログラミングの形で層に積み上げられている人々が、なぜ私は継承が多すぎるのか、なぜ合成が継承よりも優先されるべきかを疑う理由です。特にJavaプログラマーにとって問題になるようです。
12

2
いいえ、密結合は必ずしも悪いわけではありません。それはしばしば悪いことですが、それを避けるために長い時間をかけると、ソフトコーディング
メイソンウィーラー

16

抽象化に関するCode Completeのセクションを読み終えたばかりなので、このソースのほとんどがここにあります。

抽象化のポイントは、「これはどのように実装されているのか?」と尋ねる必要を取り除くことです。を呼び出すとuser.get_id()、an idが返されることになることがわかります。「これはどのようにユーザーIDを取得するのですか?」と尋ねる必要がある場合 おそらく、を必要としないかidget_id()予期しないもので設計が不十分なものを返します。

抽象化を使用して、以下を設計できます。

a house with doors and windows

設計しない

a box with four walls,
    with 3 holes,
        two of which fit panes of glass surrounded by wood frames,
        one that fits a large plank of wood with hinges and a metal knob,
etc.

私はこれらのインターフェースについて話しているのではありません。これらのインターフェースは問題ありません。私は、多くの異なる抽象化の背後にセグメント化された巨大で複雑なシステムについて話している。
user606723

9
@ user606723次に、あなたの質問はovercomplexデザインではなく、抽象化についてです
アンドレスF.

3
コード完了の場合は+1。設計に抽象化が必要な理由と、間違ったレベルの抽象化が設計を妨げる理由両方をカバーしています。あなたの例をさらに進めるために、私がゾーニングオフィスで働いているなら、詳細をいじることなく、あなたの家について考えたいと思います。しかし、あなたが私のように家について考えたなら、あなたはそれを建てることができませんでした。
スペンサー

この質問を閉じるか、タイトルを変更する必要があります
ジェイクバーガー

8

詳細を隠すことには本当の価値がありますか?

はい。抽象化を提示することで、より高いレベルで考えてプログラミングできます。

微積分や行列代数のない物理システムのモデリングを想像してください。それは完全に非実用的です。同様に、スカラーレベルでしかプログラミングできない場合、興味深い問題を解決することはできません。比較的単純なWebアプリケーションでさえ、タグライブラリのような抽象化から大きな恩恵を受けることができます。4つのテキストフィールドと選択ボックスを繰り返し作成するよりも、「アドレス入力フィールド」を意味するタグを挿入する方がはるかに簡単です。また、海外に展開する場合は、すべてのフォームを修正して国際住所を処理するのではなく、タグ定義を変更するだけで済みます。一部のプログラマーは、抽象化を効果的に使用することで、他のプログラマーの10倍の効果が得られます。

人間の作業記憶は限られています。抽象化により、大規模システムについて推論することができます。

Aren't we sacrificing transparency?

いいえ。抽象化が使用されない場合、ソフトウェアコンポーネントの目的は繰り返される詳細に埋もれます。開発者は次のようなコードを歩いて日々を過ごしています。

for (i = 0; i < pilgrim.wives.size(); ++i) {
  wife = pilgrim.wives[i];
  for (j = 0; j < wife.sacks.size(); ++j) {
     sack = wife.sacks[i];
     for (k = 0; j < sack.cats.size(); ++j) {
        cat = sack.cats[k];
        for (m = 0; m < cat.kits.size(); ++m) {
           ++count;
        }
     }
  }
}

そして、「ああ、キットを巡る別の4レベルのループ」と考える代わりに、

pilgrim.kits.each { ++count; }

この透明性は重要ではありませんか?

あなたが指摘したように、間接化にはコストがかかります。「念のため」レイヤーを作成しても意味がありません。抽象化を使用して重複を減らし、コードを明確にします。


7

抽象化が実装の詳細を隠していると人々が言うとき、それらは見つけるのを難しくするという意味で実際には「隠す」という意味ではありません。それらが意味することは、インターフェースをシンプルで簡潔で管理しやすいものにするために、パブリックインターフェースから実装の詳細を分離することです。自動車がその重要な部分のほとんどを「隠し」、それらを操作するためのかなり初歩的なコントロールセットのみを提供するように、ソフトウェアモジュールはその機能の大部分を腸の奥深くに「隠し」、限られた数のアクセス方法のみを公開しますそれを運転します。エンジンのすべての内部を手動で操作しなければならなかった車を想像してみてください(そして、非常に多くのエンジンがあります)、トラフィックを監視して道を見つけるのは本当に難しいでしょう。

しかし、インターフェイスをシンプルに保つことは、単に審美的なことではありません。プロジェクトの成功とデスマーチの違いを生むことができます。悪魔の擁護者を1分間プレイしましょう。まったく抽象化されていないソフトウェアプロジェクトを想像してください。値を保持する必要がある場合は、グローバル変数を使用します。機能を複数回使用する必要がある場合は、コピーアンドペーストします。特定のコードセクションの2つの異なるバージョンが必要な場合は、コピーifアンドペーストしてステートメントにラップし、両方のブランチを変更します。技術的には機能しますが、数か月後には、いくつかの本当に厄介な問題と戦うことになります。

  • バグを見つけて修正すると、同様のコードの他のコピーペーストされたインスタンスにも存在する可能性が高いため、バグを見つけて修正することに加えて、他の発生を探して修正する必要もあります。
  • バグを見つけたり、変更を実装したりするには、メンテナンスプログラマが関連するコードを理解できる必要があります。これを行うことの難しさは、関連するコードセクションのサイズとともに増加しますが、そのスコープによってさらに大きくなります。頭の中に6ダースの変数を保持しながら、精神的にコードをステップスルーすることはできます。しかし、それらが数百個ある場合、生産性に深刻な影響があります(思考プロセスを、物理RAMが不足し、スワップファイルに浸る必要があるプログラムと比較するのが好きです:一度に流throughにコードを読むのではなく、 、プログラマは物事を調べるために前後にジャンプする必要があります)。
  • コードの範囲は、バグを見つけるために掘り下げなければならないコードベースのサイズにも影響します。2つのパラメーターを持ち、グローバルを持たない10行の関数があり、入力の値とそれがクラッシュする行を知っている場合、バグの発見は通常些細なことであり、多くの場合、コードを見るだけで済みます。それが数百行、20個のパラメーター、15個のグローバルであり、同様の性質のいくつかの他の関数を呼び出す場合、あなたはいくつかの深刻な痛みに直面しています。
  • 適切に抽象化しないと、変更はコードベースの大部分に影響を与える可能性があります。実際には、変更されるコードに依存するものがすべてあるためです。このようなコードベースの典型的な症状は、小さな一見無害な変更を加え、まったく関係のない機能が突然壊れることです。抽象化により、変更によって発生する可能性のある損害の量を制限し、影響をより予測可能にすることができます。プライベートフィールドの名前を変更する場合、チェックするソースファイルは1つだけです。グローバル変数の名前を変更する場合は、コードベース全体を実行する必要があります。

ひどく抽象化されたコードベースでは、影響は通常、コードベースのサイズとともに指数関数的に増大します。つまり、一定量のコードを追加すると、一定の要因でメンテナンス作業が増加します。さらに悪いことに、プロジェクトにプログラマを追加しても、生産性は直線的にではなく、せいぜい対数的に増加します(チームが大きくなると、コミュニケーションにオーバーヘッドが必要になります)。


2

必要に応じてどのように機能するかを理解する必要があると思います。あなたがそれがするであろうと思ったことをそれが決定したら、それからあなたは心の安らぎを持ちます。永遠にそれを隠すことが目的だとは思いませんでした。

動作すると確信している時計にアラームを設定すると、適切な時間にアラームがオフになることを知って、ある程度の睡眠をとることができます。秒刻みを見ることができるように1時間早く目を覚ますのは無駄です。


1
確かに、目覚まし時計の動作を変更したり、変更を完全に知らされずに他の人に目覚まし時計の動作を変更したりすることはあまり求められません。
user606723

1
これまでに使用したすべてのフレームワークのコードが変更されていますか?
ジェフ

1
いいえ。ただし、これまでに使用したすべてのフレームワークのコード変更を管理していません。
user606723

3
あなたはJeffOのポイントをほとんど証明しました:あなたも目覚まし時計を維持するビジネスではありません。完全に分解して動作を分析せずに購入して使用を開始すると、内部が抽象化されることを受け入れました。それがどのように機能するかを知るためにそれを引き裂くことができないと言うこと何もありませんが、どれくらいの頻度でそれが必要であると思いますか?
-Blrfl

2

具体的に質問に答えるには:

詳細を隠すことには本当の価値がありますか?

はい。あなたの質問の最初の行で認めるように。

透明性を犠牲にしませんか?

あんまり。よく書かれた抽象化により、必要に応じて詳細を理解しやすくなります。

この透明性は重要ではありませんか?

はい。抽象化は、必要な場合や必要な場合に詳細を簡単に理解できるように設計および実装する必要があります。


最後に私が好きな答え。
user606723

1

隠されているものが機能する場合、詳細を隠すことは素晴らしいことだと思います。

たとえば、ビヘイビアを定義するインターフェイス(つまり、GetListItems、SendListItem)を開発するとします。これは、ボタンクリックなどによってユーザーが開始する2つの機能です。今、各ユーザーは独自の「ListItemStore」を持つことができます。 facebookにあるもの、myspaceにあるもの(例)..ユーザープロパティとして保存/ユーザーのprefrencesを介してアプリ内のどこかに設定します。そして、アプリの開発者が追加のListItemStoreを追加することが可能だと言います。時間(mybook、facespaceなど)

Facebookに接続してアイテムを取得する方法には多くの詳細があります。myspaceに接続するときも同様に多くの詳細があります。

現在、最初の「ストアアクセス」コードが記述された後、コードを変更する必要はないかもしれません(facebookの場合、おそらく、フルタイムの開発者が変更に追いつく必要があります。

そのため、コードを使用すると次のようになります。

    new ItemManager(user) //passes in user, allowing class to get all user properties
    ItemManager.GetListItems()

そして今、あなたはそれを保存した場所からユーザーのデータを取得しました、そして私が心配しているのはアイテムのリストを取得し、それを使って何かをすることであり、さらに多くの店舗が追加され、スタックに質問に答えたり投稿したりすることができます... lol ..

だから、それを実現するための配管はすべて「隠された」ものであり、アイテムの正しいリストを取得する限り、誰がそれをどのように行うのかを本当に気にかけます。単体テストがある場合、結果が必要なのですでに定量化されています。


ええ、しかし、維持する必要があるのは、これらの2行ではありません。それらを台無しにすることは不可能です。変更する必要があるのは、常に2番目、3番目、4番目のレベルです。安定していると信頼できるAPIがある場合、これは素晴らしいことですが、ビジネスの複雑さだけでは安定しない場合はどうでしょうか。
user606723

1
もしAPIが安定していない場合は、@ user606723、それがある可能性が高いです未熟、またはどちらかの可能性が高い間違った抽象化
SDG

@ user606723-それは事実です。そのような場合、命名規則自体が実際のプログラミングロジック/詳細を隠す何らかの透明性を定義する必要があります。したがって、本質的には、適切な名前を最後まで使用して透明性を実現できますが、実際に頻繁に下に行く必要はありません。
ハンゾロ

@sdg、またはシステムの要件が常に変化しているため。法律が変わるので、他のAPIが変わるので、それは私たちの制御外です。
user606723

1
@ user606723-私は実際に金融SaaSシステムで作業しており、リリースサイクルごとに十分な抽象化がないという落とし穴があります。その一部は設計が不十分な結果ですが、通常は元々意図されていなかった場所にコードを押し込んだ結果です。これらのコメント、名前、カプセル化がなければ、チェーンを下るのは苦痛ですです。私がしなければならなかったすべては、オーバーロードを追加し、適切な計算に到着するか、新しいものを追加するために、異なる論理分岐を読んよりも、そんなに立派になり、その後Remeasurements.GetRemeasureType(グラント)、およびreMeasure.PerformCalc()、だった場合
ハンゾロ

1

「非表示」と呼ばれるものは、多くの場合、懸念事項の分離と見なされます(たとえば、実装とインターフェース)。

私の意見では、抽象化の主な利点の1つは、開発者の限られたブレインスペースからの不要な詳細の混乱を減らすことです。

実装コードが難読化されている場合、透明性を妨げるものとして見ることができますが、私が見ているように抽象化はちょうど良い組織です。


1

まず、単一のマシンコード命令を超えるものはすべて本質的に抽象化されていwhile...doます。ループとは、条件が満たされるまで一連の命令を繰り返すために必要な比較とアドレス呼び出しを表す一貫した記号的な方法です。同様に、int型はXビット数の抽象化です(システムによって異なります)。プログラミングはすべて抽象化です。

おそらく、これらの原始的な抽象化が非常に役立つことに同意するでしょう。まあ、それはあなた自身を構築できることです。OOADとOOPがすべてです。

ユーザーが、区切りテキスト、Excel、PDFなどのさまざまな形式で画面からデータをエクスポートできるようにしたいという要件があるとします。特定の出力を作成する方法を知っているDelimitedTextExporter、ExcelExporter、およびPDFExporterを構築できるexport(data)メソッドを使用して「Exporter」というインターフェイスを作成できると便利ではないでしょうか。呼び出し元のプログラムが知っておく必要があるのは、export(data)メソッドを呼び出すことができるということだけで、どの実装を使用してもそれが行われます。さらに、区切りテキストルールが変更された場合、ExcelExporterをいじり回すことなく、おそらくそれを壊すことなく、DelimitedTextExporterを変更できます。

OOプログラミングで使用されるよく知られているデザインパターンのほとんどは、抽象化に依存しています。フリーマンとフリーマンのヘッドファーストデザインパターンを読んで、抽象化が良いことの理由をよく理解することをお勧めします


1
でもマシンコード抽象化、ロジックの下に行くtheresの本当の、物理的、プロセスであってもデジタル家電は何の上に抽象化され、実際にチップをオーバークロックのハッシュを証明することができます作られたことのある人のように起こる
JK。

あまりにも本当ですが、機械命令レベルではより具体的に見えます。
マシューフリン

1

私はこれについてあなたの気持ちを理解していると思うし、私も同様の意見を持っていると思う。

理解しやすいため、50のコードラインクラスを3つのクラスと3つのインターフェイスに変換するJava開発者と協力しました。そして我慢できませんでした。

理解するのは非常に難しく、デバッグすることはほとんど不可能で、「実装を切り替える」必要はありませんでした。

一方、複数のオブジェクトが同様の動作を共有し、1つの場所で使用されるコードも確認しました。メソッドが共通のインターフェイスを介して公開されている場合、実際には共通の並べ替え/処理ループを使用できます。

したがって、同様のシナリオで使用される可能性が高いコアオブジェクトであるIMHOは、通常、インターフェイスを介してアクセスできる一般的な動作の恩恵を受けます。しかし、それはほとんどそれであり、それは正しいから単純なものを抽象化するか、実装を切り替えることができるようにすることは、コードを煩雑にするための単なる方法です。

繰り返しになりますが、ライフタイム管理に関するすべての問題を抱えた爆発的な量の小さなクラスよりも、よりスマートなクラスを好みます。そのため、一部の人々は私に反対します。


1

隠蔽と抽象化のガイド目的は、ユーザーを実装から切り離すことで、独立して変更できるようにする必要があります。または将来のより良いアルゴリズム。

モジュールを書くとき、実装の隠された部分は、あなたが考えることができない他のコードを壊す危険を冒すことなくそれらを変更できるという心の一部を与えます。

不透明なインターフェイスを提供するもう1つの利点は、サブシステム間の表面積が大幅に減少することです。相互作用できる方法の量を減らすことで、より予測可能になり、テストが容易になり、バグが少なくなります。モジュール間の相互作用もモジュールの数とともに二次的に増加するため、この複雑さの増大を制御しようとすることには価値があります。


それは言うまでもなく、あまりにも多くを隠し、インターフェースを深く入れ子にすることは可能です。インテリジェントな人間としてのプログラマーの仕事は、複雑さを最小限に抑え、保守性を最大限に高めながら、最大限に役立つようにシステムを設計することです。


0

多くの場合、物事の実装方法を知る必要ありません。このpeople.Where(p => p.Surname == "Smith")ようなコードを1日に何度も書くことはほぼ保証できますが、「このWhere()方法が実際にどのように機能するのか」と考えることはほとんどありません。気にしないでください-このメソッドが存在し、必要な結果が得られることがわかっています。なぜあなたはそれがどのように機能するか気にしますか?

これは、社内ソフトウェアでもまったく同じです。OracleやMicrosoftなどによって書かれていないからといって、それがどのように実装されているかを探さなければならないという意味ではありません。メソッドが呼び出さGetAllPeopleWithSurname(string name)れて、その姓を持つ人のリストが返されることを合理的に期待できます。リストを反復処理したり、辞書を使用したり、まったくおかしなことをしたりするかもしれませんが、気にする必要はありません

もちろん、このルールには例外があります(ルールがなければルールはありません!)、それはメソッドにバグがある場合です。そのため、上記の例では、3人のリストがあり、そのうちの1人が姓Smithを持ち、リストに返されないことがわかっている場合、そのメソッドの実装は明らかに壊れているため気になります。 。

アブストラクションを正しく実行すると、後日読む必要があるときに役に立たないものをすべて除外できるため、素晴らしいです。コードを書くのに費やすよりもはるかに多くの時間がコードの読み取りに費やされることを忘れないでください。したがって、そのタスクを可能な限り簡単にすることに重点を置く必要があります。また、抽象化は腕と同じくらい長いオブジェクトの階層を意味するが、100行のメソッドをそれぞれ10行の長さの10メソッドにリファクタリングするのと同じくらい簡単であると考えるかもしれません。かつてはすべて10のステップが一緒にバンドルされていたものが、10の個別のステップになり、この厄介なバグが隠れている場所に直接移動できるようになりました。


ええ、しかしあなたが実装を維持している人だと言ってみましょう。つまり、誰かが実装を気にし、その人があなたです。また、実装のユーザーになることもあります。ビジネス要件が(予測できない方法で)変更されると、多くのレイヤーで変更が発生します。私のポイントは、バグがそのルールの唯一の例外ではないということです。
user606723

問題はPeopleFactory.People.Strategy.MakePeople.(CoutryLaw.NameRegistry.NameMaker.Make()) as People.Female
コーダー

@ user606723コードベースのコードのすべての行を常に気にする必要はありません。その実装にバグがあるか、再書き込みが必要であるとフラグが付けられている場合(遅い、書き方が悪いなど)、書き直している場合は、それを気にします。それ以外の場合は、邪魔にならないようにしてください。たぶんあなたの問題は、あなたが常にすべてのコードを所有しようとしていることです。私の意見では、特定の時間に作業しているコードに集中する必要があります。
スチュアートレイランドコール

@Coder明らかにそれは非常に極端な形の抽象化です!最初はOPが反対するようなものだと思っていましたが、残りの返信を読むと、すべての抽象化が悪い悪い悪いと見なされているようです。これが、答えの中で抽象化のさまざまなレベルを説明しようとした理由です。
スチュアートレイランドコール

0

抽象化により、情報が隠されます。これにより、カップリングが低くなります。これにより、変更のリスクが少なくなります。これは、コードに触れるときに緊張することなく、幸せなプログラマにつながるはずです。

これらのアイデアは、ソフトウェアアーキテクチャの3つの重要な法律によって表されます。

サイモンの法則:「階層は複雑さを軽減します。」(階層は抽象化を導入します)

パルナの法則:「隠されているものだけがリスクなしで変更できます。」

コンスタンタンの法則:堅牢なプログラムには低結合と高凝集度が必要


「階層は複雑さを軽減します。」-必ずしも真実ではありません。
コーダー

決定論的な設計方法論はありません。この法則はIT / CSから派生したものではなく、はるかに広い意味で定式化されており、数学、物理学者などによっても参照されています。
ins0m

0

私もエンタープライズアプリケーションビジネスに携わっており、この質問には私自身も同じ質問があるので注意を引きました。これまでの私のキャリアの中で、抽象化の問題に関する洞察をいくつか得ましたが、私の洞察は決して普遍的な答えではありません。私は新しいアイデアや考えを学び続け、聞き続けているので、今私が信じるものは変わるかもしれません。

私が大規模で複雑な医療アプリケーションを保守していたとき、ちょうどあなたが好きだったとき、私はそこにあるすべての抽象化が嫌いでした。すべてのコードがどこに行くかを考えることは、首の痛みでした。さまざまなクラスを飛び回ってめまいがした。だから私は自分自身に「抽象化はダメ、物をデザインするときは抽象化を最小限に抑える」と言った。

次に、アプリケーション(比較的小さなWebサービスコンポーネント)をゼロから設計しなければならなかったときがきました。すべての痛みを覚えて、私はコンポーネントのかなりフラットなデザインを持っていました。問題は、要件が変更されたときに、多くの異なる場所に変更を加える必要があったことです(要件は非常に流動的であり、それについては何もできませんでした)。とてもひどかったので、基本的には最初のデザインを捨てて、抽象化して再デザインしました。状況は良くなりました。要件が変わっても、多くの場所を変更する必要はありませんでした。

アプリケーションを配信し、数週間座ってから、アプリケーションのメンテナンスを開始するように言われました。私はしばらくの間、すべてを覚えていなかったので、自分のコードを理解するのに少し苦労しましたが、抽象化は助けにはなりませんでした。

その後、私は他の多くのさまざまなプロジェクトに参加し、抽象化レベルをもう少し試す機会がありました。私が実際に見つけたのは、これは私の個人的な意見です。抽象化は開発に大いに役立ちますが、コードを記述せず、アプリケーションの最も深いレベルまですべてを理解しようとするとマイナスの影響があります。さまざまなクラスを飛び回って、接続を確立するのにより多くの時間を費やすことになります。

私の考えでは、抽象化は開発時に非常に価値があるので、コードを理解しようとするときにメンテナーとして経験するトラブルは価値があります。ソフトウェアはビジネス上の問題を解決するために存在し、ビジネス上の問題は時間とともに進化します。したがって、ソフトウェアは時間とともに進化する必要があります。抽象化なしでは、ソフトウェアの進化は非常に困難です。メンテナーがコード構造のパターンを見たら簡単にコードベースをナビゲートできるように抽象化を設計できるため、最初の学習曲線だけがイライラします。


0

他の人が言ったように、抽象化の背後にある「詳細を隠す」ことで、ユーザーに影響を与えることなくそれらを変更できます。この考え方は、Parnasの「システムをモジュールに分解する際に使用される基準について(1972)に基づいており、抽象データ型(ADT)およびオブジェクト指向プログラミングの考え方に関連しています。

ほぼ同時に、Coddの大規模共有データバンクのデータのリレーショナルモデル(1970年)は、データベースのユーザーに影響を与えることなく、データベースの内部ストレージ表現を変更したいという動機付けがありました(要約とイントロを参照)。彼はプログラマーが定期的に数日かけてコードのページを変更し、小さなストレージの変更に対処するのを見てきました。

とはいえ、抽象化は、それを使用できるようにするために内部にあるものを確認する必要がある場合、あまり有用ではありません。うまく設計することは非常に困難です。優れた抽象化の例は追加です-内部で何が起こるかについて最後に考えなければならなかったのはいつですか?(ただし、オーバーフローする場合など)。

本質的な問題(IMHO)は、モジュールを(Parnasの意味で)うまく設計するには、何が変化し、何が変化しないかを予測する必要があるということです。未来を予測することは困難です-しかし、何かについて多くの経験があり、それを明確に理解していれば、かなり良い予測をすることができます。したがって、適切に機能するモジュール(抽象化)を設計できます。

しかし、最終的には抽象化の破壊を必要とする予期せぬ(そしてほぼ間違いなく予測不可能な)変更が行われるのは、すべての抽象化の運命であり、最高の場合でもそうです。これに対処するために、一部の抽象化にはエスケープがあり、本当に必要な場合はより深いレベルにアクセスできます。

これはすべて非常に否定的なようです。しかし、私は、私たちはそれらに気づかない、またはそれらが隠れていることを理解しないほどうまく機能する抽象化に囲まれていると思います。不十分な抽象化にのみ気付くので、それらについては慎重な見方をしています。


0

抽象化は、主に消費者(アプリケーションプログラマなど)の利益のためです。システム(設計者)プログラマーは、それらを美しく有用にするためにより多くの作業を行う必要があります。そのため、通常、初心者は優れた設計を行いません。

抽象化は常に複雑さを増すため、抽象化は好きではないでしょうか?作業したシステムに抽象化があったのかもしれません(抽象化の過剰使用)。それらは万能薬ではありません。

便利な抽象化の余分な作業と複雑さは報われるはずですが、確実に知ることは困難です。抽象化をピボットポイントと考えると、ソフトウェア設計はどちらの側にも柔軟に対応できます。抽象化の実装は、クライアントコードを壊すことなく変更でき、新しいクライアントは抽象化を簡単に再利用して新しいものを作成できます。

抽象化の投資収益率は、比較的痛みのない実装の変更と新しいクライアントの追加のいずれかまたは両方の方向に時間をかけて「フレックス」されていることを示すことで、ほぼ測定できます。

たとえば、JavaのSocketクラスの抽象化を使用すると、Java 1.2のアプリケーションコードはJava 7でも正常に動作するはずです(ただし、パフォーマンスが多少変更される可能性があります)。Java 1.2以降、この抽象化を使用する新しいクライアントも間違いなく存在しています。

抽象化に関する不幸については、Socketクラスの背後にあるコードを保守している開発者と話をした場合、Socketを使用して楽しいアプリケーションを作成したクライアントほど、彼らの人生は桃色でバラ色ではありません。抽象化の実装に取り​​組むことは、確かにそれを使用するよりも多くの仕事です。しかし、それで悪くなるわけではありません。

透明性に関しては、トップダウンの設計戦略では、完全な透明性が悪い設計になります。賢いプログラマーは、自由に使える情報を最大限に活用する傾向があり、システムは緊密に結合されます。モジュールの詳細の最小の変更(データ構造内のバイトの順序の変更など)は、スマートプログラマーがその情報を使用して有用なことを行うため、他のコードを壊す可能性があります。デビッドパルナスは、1971年の古い記事でこの問題を指摘し、デザインに情報を隠すことを提案しました。

ArchLinuxへの言及は、オペレーティングシステムの「内部」が、その上で実行されるアプリケーションに対するOSである抽象化の複雑な実装であると考える場合に意味があります。抽象化の根底でシンプルに保ちます。


0

質問に答えます。今朝仕事に行ったとき(実際に仕事をしたと思います)、燃料と空気の混合物を入れるためにエンジンがバルブを開く方法を正確に気にしましたか?いいえ。道路を走行しているとき、車のエンジンがどのように機能するは気にしません。あなたはそれ機能することを気にます。

ある日、あなたの車が機能しなくなったとします。開始せず、ロッドを投げ、ベルトを破り、あなたがテキストメッセージで忙しい間、あなた自身の過失なしに不可解にそのコンクリートの障壁に耕します。今、あなたは(少なくとも一時的に)新しい車が必要です。この新しい車の仕組みを正確に気にしますか?いいえ。まず気にするのは、それが機能することです。次に、古い車を運転するのに使用したのと同じ知識とスキルを使用して新しい車を運転できることです。理想的には、運転している車に変化はなかったように見えるはずです。現実的には、この新しい車が機能する方法は、可能な限り少ない「驚き」を与えるはずです。

これらの基本原則は、カプセル化と抽象化の背後にある中核となる原則です。オブジェクトがそれを行う方法を知っていることは、それを使用してそれを行うための必須条件ではありません。コンピュータープログラミングでも、プログラムを実行しているCPU内の電気経路の詳細は、少なくとも半ダースのI / O命令、ドライバー、OSソフトウェア、およびランタイムのレイヤーの背後で抽象化されます。非常に成功している多くのソフトウェアエンジニアは、それを実行する正確なハードウェアアーキテクチャやOSビルドについても心配することなく、完全に優れたコードを記述します。私を含む。

カプセル化/情報の隠蔽により、「どうするかは気にせず、気にするだけ」という考え方ができます。オブジェクトは、消費者が簡単に消費できるように、消費者にとって有用なものを公開する必要があります。さて、現実の世界に戻って、これは車がユーザーに内部の仕組みに関する情報を与えてはならないという意味ではなく、車はユーザーにイグニッション、ハンドル、とペダル。すべての車には、スピードメーターと燃料計、タコメーター、馬鹿灯、その他のフィードバックがあります。事実上すべての車には、ヘッドライト、方向指示器、ラジオ、座席調整などのさまざまな独立したサブシステム用のスイッチもあります。すべての場合において、十分な知識があれば、それを開いて、少し異なる方法で動作するように変更することができます。しかし、ほとんどの場合、多分、たぶん、ユーザーはキャビン内から燃料ポンプを直接かつ独立して制御することはできませんか?たぶん、たぶん、ユーザーは実際にブレーキペダルを踏まなければブレーキライトを作動させるべきではないでしょうか?

抽象化により、「これはそれと同じではありませんが、どちらもXIなので、Xと同じように使用できます」という考え方が可能です。オブジェクトが抽象化を継承または実装する場合、コンシューマーは、実装が抽象化の他の既知の実装と同じまたは同様の結果を生成することを期待する必要があります。トヨタカムリとフォードフュージョンはどちらも「車」です。そのため、ステアリングホイールなど、予想される機能の共通セットがあります。左に回すと左に曲がります。時計回りに回すと、車は右に進みます。米国のどの車にも乗ることができ、車にはハンドルと少なくとも2つのペダルがあります。右側の1つは「車が行く」ペダルで、中央の1つは「車が止まる」ペダルです。

抽象化の帰結は「最小限の驚きの理論」です。試乗のために新しい車のハンドルを握り、ハンドルを時計回りに回し、車が左に曲がった場合、控えめに言ってもかなり驚くでしょう。ディーラーがPOSを行商していると非難し、新しい振る舞いが以前よりも「良い」理由、またはこの振る舞いが「文書化されている」または「どのように」制御システムは透明です。この新しい車とあなたが運転した他のすべての車はまだ「車」ですが、この車を運転するときは、新しい車をうまく運転するために、車の運転方法に関する基本的な概念を変更する必要があります。それは通常悪いことです、そして、それは新しいパラダイムに直感的な利点がある場合にのみ起こります。おそらく、シートベルトの追加が良い例です。50年前、あなたは乗り込んだばかりでしたが、今は腰を据える必要があります。直観的な利点は、事故に遭ってもフロントガラスや助手席に行かないことです。それでも、ドライバーは抵抗しました。多くの自動車所有者は、使用を義務付ける法律が可決されるまで、自動車からシートベルトを切り取りました。

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