基本的に私たちは物事が賢明に振る舞うことを望みます。
次の問題を考慮してください。
長方形のグループが与えられ、その面積を10%増やしたいです。だから私は、長方形の長さを以前の1.1倍に設定しました。
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
rectangle.Length = rectangle.Length * 1.1;
}
}
この場合、すべての長方形の長さが10%増加し、面積が10%増加します。残念ながら、実際に誰かが私に正方形と長方形の混合物を渡し、長方形の長さが変更されると幅も変更されました。
すべての単体テストを作成して四角形のコレクションを使用するため、単体テストに合格します。私は今、アプリケーションにわずかなバグを導入しました。
さらに悪いことに、会計のジムは私の方法を見て、彼が私の方法に正方形を渡すと、彼が非常に素晴らしい21%のサイズを得るという事実を使用する他のコードを書きます。ジムは幸せで、誰も賢くありません。
ジムは、優れた仕事のために別の部門に昇進します。アルフレッドはジュニアとして入社しました。AdvertisingのJillは最初のバグレポートで、このメソッドに正方形を渡すと21%増加し、バグを修正することを望んでいると報告しています。アルフレッドは、正方形と長方形がコードのあらゆる場所で使用されていることを認識し、継承チェーンを破ることは不可能であることを認識しています。彼はまた、経理のソースコードにアクセスできません。したがって、Alfredはこのバグを次のように修正します。
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle)
{
rectangle.Length = rectangle.Length * 1.1;
}
if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
アルフレッドは彼の非常にハッキングのスキルに満足しており、ジルはバグが修正されたことを承認します。
来月は、会計がIncreaseRectangleSizeByTenPercent
メソッドに正方形を渡すことができ、面積が21%増加することに依存していたため、誰も支払いを受けません。会社全体が「優先度1バグ修正」モードに入り、問題の原因を突き止めます。彼らはアルフレッドの修正に問題をトレースします。彼らは、会計と広告の両方を満足させなければならないことを知っています。そこで、次のようなメソッド呼び出しでユーザーを識別することで問題を修正します。
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles)
{
IncreaseRectangleSizeByTenPercent(
rectangles,
new User() { Department = Department.Accounting });
}
public void IncreaseRectangleSizeByTenPercent(IEnumerable<Rectangle> rectangles, User user)
{
foreach(var rectangle in rectangles)
{
if (typeof(rectangle) == Rectangle || user.Department == Department.Accounting)
{
rectangle.Length = rectangle.Length * 1.1;
}
else if (typeof(rectangle) == Square)
{
rectangle.Length = rectangle.Length * 1.04880884817;
}
}
}
などなど。
この逸話は、プログラマーが毎日直面する現実の状況に基づいています。リスコフの置換原則の違反は違反を固定し、その時点で彼らだけが書かれている数年後に拾わ取得は非常に微妙なバグを導入することができ、物事の束を破るとなります固定ではない、それはあなたの最大のクライアントを怒らます。
この問題を修正するには、2つの現実的な方法があります。
最初の方法は、Rectangleを不変にすることです。RectangleのユーザーがLengthおよびWidthプロパティを変更できない場合、この問題はなくなります。異なる長さと幅の長方形が必要な場合は、新しい長方形を作成します。正方形は、長方形からうまく継承できます。
2番目の方法は、正方形と長方形の間の継承チェーンを分割することです。正方形が単一のSideLength
プロパティを持ち、長方形がLength
and Width
プロパティを持ち、継承がないと定義されている場合、長方形を期待して正方形を取得することで誤って物事を壊すことは不可能です。C#の用語ではseal
、四角形クラスを使用して、取得するすべての四角形を実際に四角形にすることができます。
この場合、問題を修正する「不変オブジェクト」の方法が好きです。長方形の正体は、その長さと幅です。オブジェクトのIDを変更する場合、本当に必要なのは新しいオブジェクトであることは理にかなっています。古い顧客を失って新しい顧客を獲得した場合、Customer.Id
フィールドを古い顧客から新しい顧客に変更せずに、新しいを作成しますCustomer
。
Liskov Substitutionの原則に対する違反は現実の世界では一般的です。それは主に、多くのコードが無能である/時間のプレッシャーにさらされている/気にしない/間違える人によって書かれているためです。それはいくつかの非常に厄介な問題を引き起こす可能性があります。ほとんどの場合、代わりに継承よりも合成を優先します。