リヒターの代替原則の定義
Liskov Substitution Principle(LSP)は、MITコンピュータサイエンスラボラトリのLiskov氏が1987年の「オブジェクト指向テクノロジサミット」(OOPSLA)で発表した記事です。 「データの抽象化と階層」(データの抽象化と階層)、彼女は提案しました:継承は、スーパータイプオブジェクトについて証明されたすべてのプロパティがサブタイプオブジェクトにも適用されることを保証する必要があります)。
リヒターの代替原則は、主に継承に関するいくつかの原則、すなわち、継承を使用する必要がある場合と使用しない場合、およびその背後にある原則を詳しく説明しています。リヒター置換は元々継承と再利用の基礎でしたが、これは基本クラスとサブクラスの関係を反映し、開閉の原理の補足であり、抽象化を実現するための具体的な手順の仕様でした。
リヒター置換原理の役割
リヒター置換原理の主な機能は次のとおりです。
- リヒター置換原理は、開閉原理を実現するための重要な方法の1つです。
- 継承で親クラスをオーバーライドすることによって引き起こされる再利用性の不足の欠点を克服します。
- それは行動の正確さの保証です。つまり、クラスの拡張によって既存のシステムに新しいエラーが発生することはなく、コードエラーの可能性が低くなります。
リヒター置換原理を実装する方法
一般に、リヒター置換の原則は次のとおりです。サブクラスは親クラスの関数を拡張できますが、親クラスの元の関数を変更することはできません。つまり、サブクラスが親クラスを継承する場合、新しい関数を追加する新しいメソッドを追加することに加えて、親クラスのメソッドをオーバーライドしないようにしてください。
親クラスのメソッドを書き換えて新しい関数を完成させると、簡単に書くことができますが、継承システム全体の再利用性は比較的低くなります。特に、ポリモーフィズムが頻繁に使用される場合、プログラム操作エラーの確率は非常に大きくなります。
プログラムがリヒター置換の原則に違反している場合、継承されたクラスのオブジェクトは、基本クラスが表示される場所でエラーになります。この時点での修正方法は、元の継承関係をキャンセルして、それらの間の関係を再設計することです。
リヒター置換の原則の例に関して、最も有名なものは「正方形は長方形ではない」です。もちろん、人生には似たような例がたくさんあります。たとえば、ペンギン、ダチョウ、キウイは生物学的な観点から分類されています。これらは鳥に属していますが、クラスの継承関係の観点から見ると、「鳥」は継承できないため、関数なので、「鳥」のサブカテゴリとして定義することはできません。同様に、「バルーンフィッシュ」は泳ぐことができないため、「魚」のサブタイプとして定義することはできません。「おもちゃの大砲」は敵を爆破することができないため、「大砲」のサブタイプとして定義することはできません。
リヒター置換の原理を説明するために、例として「キウイは鳥ではない」としましょう。
【事例2】「キウイは鳥ではない」の事例におけるリヒター代入原理の適用。
分析:通常、鳥は飛ぶため、ツバメの速度は時速約120キロです。ただし、ニュージーランドのキウイは翼があるため飛行できません。例を設計する場合は、これらの2羽の鳥が300キロ飛行するのにかかる時間を計算します。明らかに、ツバメを使用してこのコードをテストすると、結果は正しく、必要な時間を計算できます。ただし、キウイ鳥を使用してテストすると、結果は「ゼロによる除算」または「無限大」になり、明らかに期待を満たしません。クラス図図1に示すように。
図1「キウイは鳥ではない」例のクラス図
プログラムコードは次のとおりです。
package principle;
public class LSPtest {
public static void main(String[] args) {
Bird bird1=new Swallow();
Bird bird2=new BrownKiwi();
bird1.setSpeed(120);
bird2.setSpeed(120);
System.out.println("如果飞行300公里:");
try {
System.out.println("燕子将飞行"+bird1.getFlyTime(300)+"小时.");
System.out.println("几维鸟将飞行"+bird2.getFlyTime(300)+"小时。");
} catch(Exception err) {
System.out.println("发生错误了!");
}
}
}
// 鸟类
class Bird {
double flySpeed;
public void setSpeed(double speed) {
flySpeed=speed;
}
public double getFlyTime(double distance) {
return(distance/flySpeed);
}
}
// 燕子类
class Swallow extends Bird{}
//几维鸟类
class BrownKiwi extends Bird {
public void setSpeed(double speed) {
flySpeed=0;
}
}
プログラムの結果は次のとおりです。
如果飞行300公里:
燕子将飞行2.5小时.
几维鸟将飞行Infinity小时。
程序运行错误的原因是:几维鸟类重写了鸟类的 setSpeed(double speed) 方法,这违背了里氏替换原则。正确的做法是:取消几维鸟原来的继承关系,定义鸟和几维鸟的更一般的父类,如动物类,它们都有奔跑的能力。几维鸟的飞行速度虽然为 0,但奔跑速度不为 0,可以计算出其奔跑 300 千米所要花费的时间。其类图如图 2 所示。
図2「キウイは動物です」の例のクラス図