「リーク抽象化」という用語はどういう意味ですか?(例を挙げて説明してください。私は、単なる理論を理解するのに苦労することがよくあります。)
「リーク抽象化」という用語はどういう意味ですか?(例を挙げて説明してください。私は、単なる理論を理解するのに苦労することがよくあります。)
回答:
これがミートスペースの例です:
自動車にはドライバー向けの抽象概念があります。最も純粋な形では、ハンドル、アクセル、ブレーキがあります。この抽象化は、ボンネットの下にあるもの(エンジン、カム、タイミングベルト、スパークプラグ、ラジエーターなど)に関する多くの詳細を隠します。
この抽象化の優れた点は、ユーザーを再トレーニングすることなく、実装の一部を改善された部分に置き換えることができることです。ディストリビューターキャップを電子点火に交換し、固定カムを可変カムに交換するとします。これらの変更によりパフォーマンスは向上しますが、ユーザーは引き続きホイールを操作し、ペダルを使用して開始および停止します。
それは実際には非常に注目に値します... 16歳または80歳は、内部でどのように機能するかをあまり知らなくても、この複雑な機械を操作できます。
しかし、リークがあります。トランスミッションは小さな漏れです。オートマチックトランスミッションでは、ギアを切り替えるときに車のパワーが一瞬失われるのを感じることができますが、CVTでは、ずっとスムーズなトルクを感じることができます。
より大きなリークもあります。エンジンの回転が速すぎると、損傷する可能性があります。エンジンブロックが冷たすぎると、車が始動しないか、性能が低下する可能性があります。また、ラジオ、ヘッドライト、エアコンを同時にクランクすると、燃費が低下します。
これは単に、抽象化によって実装の詳細の一部が公開されること、または抽象化を使用するときに実装の詳細に注意する必要があることを意味します。この用語は、2002年頃のJoel Spolskyによるものです。詳細については、ウィキペディアの記事を参照してください。
典型的な例は、リモートファイルをローカルとして扱うことができるネットワークライブラリです。この抽象化を使用する開発者は、ネットワークの問題により、ローカルファイルとは異なる方法でこれが失敗する可能性があることに注意する必要があります。次に、ネットワークライブラリが提供する抽象化以外のエラーを具体的に処理するコードを開発する必要があります。
.NET開発者によく知られている例を次に示します。ASP.NETのPage
クラスは、HTTP操作の詳細、特にフォームデータの管理を非表示にしようとするため、開発者は投稿された値を処理する必要がありません(フォーム値をサーバーに自動的にマップするため)コントロール)。
しかし、最も基本的な使用シナリオを超えてさまよった場合、Page
抽象化がリークし始め、クラスの実装の詳細を理解しない限り、ページを操作するのが難しくなります。
一般的な例の1つは、ページにコントロールを動的に追加することです。動的に追加されたコントロールの値は、適切なタイミングで追加しない限り、マップされません。基になるエンジンが受信フォームの値を適切なコントロールにマップする前です。あなたがそれを学ばなければならないとき、抽象化は漏れました。
まあ、ある意味では、それは純粋に理論的なものですが、重要ではありません。
抽象化を使用して、物事を理解しやすくします。個々のアイテムである順序付けられた文字のセットを扱っているという事実を隠すために、ある言語の文字列クラスを操作する場合があります。数字を扱っているという事実を隠すために、順序付けられた文字のセットを扱います。私は1と0を扱っているという事実を隠すために数字を扱っています。
リークのある抽象化とは、隠そうとしている詳細を隠さない抽象化です。Javaまたは.NETで5文字の文字列でstring.Lengthを呼び出すと、実装の詳細により、これらの言語が文字と呼ぶものが実際には1または1またはいずれかを表すことができるUTF-16データポイントであるため、5から10までの任意の回答を得ることができます。文字の.5。抽象化がリークされました。ただし、リークしないということは、長さを見つけるには、(実際の長さを格納するために)より多くのストレージスペースが必要になるか、(実際の長さを計算するために)O(1)からO(n)に変更する必要があることを意味します。私が本当の答えを気にするなら(あなたは本当にそうしないことが多いです)、あなたは実際に何が起こっているのかについての知識に取り組む必要があります。
より議論の余地のあるケースは、メソッドまたはプロパティが内部の仕組みに入ることができる場合、それらが抽象化リークであるか、またはより低いレベルの抽象化に移行するための明確に定義された方法であるかなど、人々が同意しない問題になることがあります。
RPCを使用して例を挙げていきます。
RPCの理想的な世界では、リモートプロシージャコールはローカルプロシージャコールのように見える必要があります(または、話は続きます)。それは、それらが呼び出すときことをプログラマに完全に透明でなければならないSomeObject.someFunction()
場合、彼らは見当がつかないSomeObject
(またはちょうどsomeFunction
そのことについては)ローカルに保存され、実行またはリモートに格納され、実行されます。これによりプログラミングが簡単になるという理論があります。
ローカル関数呼び出しを行うこと(世界で最も遅いインタープリター言語を使用している場合でも)と次のことには大きな違いがあるため、現実は異なります。
時間だけでも、それは約3桁(またはそれ以上!)の大きさの違いです。これらの3桁以上の大きさは、パフォーマンスに大きな違いをもたらし、RPCを実際の関数呼び出しとして誤って初めて扱ったときに、プロシージャコールの抽象化がリークすることは明らかです。さらに、コードに重大な問題がない限り、実際の関数呼び出しでは、実装のバグ以外の障害点はほとんどありません。RPC呼び出しには、通常の市内呼び出しに期待する以上の障害ケースとして発生する可能性のある次のすべての問題があります。
これで、「ローカル関数呼び出しと同じように」RPC呼び出しに、ローカル関数呼び出しを行うときに対処する必要のない追加の障害状態が大量に発生します。抽象化はさらに困難になりました。
結局、RPCは、成功したときと失敗したときの両方で、すべてのレベルでふるいのようにリークするため、悪い抽象化です。
サンプルAPIの使用法で、Publicationオブジェクトを多対多属性に追加する前に、ベースのArticleオブジェクトa1を.save()する必要があることに注意してください。また、多対多属性を更新すると、基になるデータベースにすぐに保存されますが、単一属性の更新は、.save()が呼び出されるまでデータベースに反映されないことに注意してください。
抽象化は、単一値属性と複数値属性が単なる属性であるオブジェクトグラフを使用しているということです。しかし、リレーショナルデータベースに裏打ちされたデータストアとしての実装はリークします... RDBSの整合性システムは、オブジェクトインターフェイスの薄いベニヤを通して表示されます。
まず、「抽象化」とは何かを理解するのが最善ですか?
抽象化は、世界を単純化する方法です。それはあなたが実際にボンネットの下/カーテンの後ろで何が起こっているのかを心配する必要がないことを意味します。それは何かがばか証拠であることを意味します。さて、それはどういう意味ですか?これは例によって最もよく示されています。
抽象化の例:737/747型機の飛行の複雑さは「抽象化」されています
ボーイングの旅客機を例にとってみましょう。これらの飛行機は非常に複雑な機械です。あなたはジェットエンジン、酸素システム、電気システム、着陸装置システムなどを持っていますが、パイロットはジェットエンジンの複雑さを心配する必要はありません....すべてが「抽象化」されています。つまり、最後にその日、パイロットは飛行機を制御するためのホイールと操縦桿についてのみ心配します。左に移動して左に移動し、右に移動して右に移動し、引き上げて高度を取得し、押し下げて下降します。それは十分に単純です......実際に私は嘘をつきました:ハンドルを制御することはもう少し複雑です。理想的な世界では、それが彼がすべき唯一のことです心配する。しかし、これは実際には当てはまりません。飛行機がどのように動作するか、または実装の詳細をまったく理解せずにサルのように飛行機を飛ばすと、墜落して搭乗している全員を殺す可能性があります。
実際には、パイロットは多くの重要なことを心配する必要があります-すべてが抽象化されているわけではありません:パイロットは風速、推力、迎え角、燃料、高度、天候の問題、降下の角度、パイロットは正しい方向に進んでおり、飛行機は現在の方向に進んでいます。コンピューターはこれらのタスクでパイロットを支援することができますが、すべてが自動化/簡素化されているわけではありません。
たとえば、パイロットがコラムを強く引き上げすぎると、飛行機は従いますが、パイロットは飛行機を失速させる危険があります。飛行機を失速させた場合、地面に衝突する前に制御を取り戻すのは非常に困難です。 。
言い換えれば、パイロットが他に何も知らずにハンドルを制御するだけでは十分ではありません......... nooooo .......彼女は飛行機の根本的なリスクと制限について知っている必要があります彼が飛ぶ前に.......彼女は飛行機がどのように機能するか、そして飛行機がどのように飛ぶかを知らなければなりません。彼は実装の詳細を知っている必要があります.....彼女は強く引き上げすぎると失速につながること、またはあまりにも急に着陸すると飛行機が破壊されることを知っている必要があります。
それらのものは抽象化されていません。多くのものが抽象化されていますが、すべてではありません。パイロットは、ステアリングコラムと、おそらく1つか2つの他のことだけを心配する必要があります。抽象化は「リーク」です。
......それはあなたのコードでも同じことです。基礎となる実装の詳細がわからない場合は、多くの場合、隅に追いやられます。
コーディングの例を次に示します。
ORMは、データベースクエリを処理する際の多くの面倒を抽象化しますが、次のようなことをしたことがある場合は次のようになります。
User.all.each do |user|
puts user.name # let's print each user's name
end
そうすれば、数百万人以上のユーザーがいる場合、それがアプリを強制終了するための優れた方法であることがわかります。すべてが抽象化されるわけではありません。User.all
2500万人のユーザーとの通話はメモリ使用量を急増させ、問題を引き起こすことを知っておく必要があります。あなたはいくつかの根本的な詳細を知る必要があります。抽象化は漏れています。
スケールと実行によって導かれるある時点で、抽象化フレームワークがそのように動作する理由を理解するために、抽象化フレームワークの実装の詳細に精通する必要があるという事実。
たとえば、次のSQL
クエリについて考えてみます。
SELECT id, first_name, last_name, age, subject FROM student_details;
そしてその代替案:
SELECT * FROM student_details;
現在、これらは論理的に同等のソリューションのように見えますが、個々の列名の指定により、最初のソリューションのパフォーマンスが向上しています。
これは些細な例ですが、最終的にはJoelSpolskyの引用に戻ります。
すべての重要な抽象化は、ある程度、漏れがあります。
ある時点で、操作で特定の規模に達すると、DB(SQL)の動作方法を最適化する必要があります。そのためには、リレーショナルデータベースがどのように機能するかを知る必要があります。最初は抽象化されていましたが、リークがあります。あなたはある時点でそれを学ぶ必要があります。
ライブラリに次のコードがあると仮定します。
Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
//fetch Device Color and Device Model from DB.
//create new Object[] and set 0th field with color and 1st field with model value.
}
コンシューマーがAPIを呼び出すと、Object []を取得します。消費者は、オブジェクト配列の最初のフィールドに色の値があり、2番目のフィールドがモデル値であることを理解する必要があります。ここで、抽象化がライブラリからコンシューマーコードにリークされました。
解決策の1つは、デバイスのモデルと色をカプセル化したオブジェクトを返すことです。コンシューマーはそのオブジェクトを呼び出して、モデルと色の値を取得できます。
DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
//fetch Device Color and Device Model from DB.
return new DeviceColorAndModel(color, model);
}
リークのある抽象化とは、状態をカプセル化することです。リークのある抽象化の非常に単純な例:
$currentTime = new DateTime();
$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);
class BankAccount {
// ...
public function setLastRefresh(DateTimeImmutable $lastRefresh)
{
$this->lastRefresh = $lastRefresh;
} }
そして正しい方法(漏れのない抽象化ではない):
class BankAccount
{
// ...
public function setLastRefresh(DateTime $lastRefresh)
{
$this->lastRefresh = clone $lastRefresh;
}
}
詳細はこちら。