オブジェクトは、コードの正確さに影響を与えることなく、そのサブタイプで置き換え可能である必要があります
これを継承(Is-a関係)で理解しましょう
例: ダチョウは鳥、せむしは車など
例: レーシングカーは車です
public class Car{ public double getCabinWidth(){ //return cabin width } }
public class RacingCar extends Car{ @Override public double getCabinWidth(){ //UNIMPLEMENTED } public double getCockpitWidth(){ //return the cockpit width of the racing car } }
RacingCar は車クラスの getCabinWidth() をオーバーライドしますが、未実装のままにしておきます。これは、レーシング カーにはキャビン幅がないためです (F1 レーシング カーを見ると、内部スペースがありません。あるのはドライバーが座るコックピットだけです)
したがって、レーシングカーの室内空間はコックピットと呼ばれます。
注: レーシングカーには一般的な車と一致しない仕様がある可能性があります
public class CarUtil{ Car car1 = new Car(); Car car2 = new Car(); Car car3 = new RacingCar(); List<Car> myCars = new ArrayList<>(); myCars.add(car1); myCars.add(car2); myCars.add(car3); // this will not work in 3rd iteration, because the getCabinWidth() in RacingCar is not implemented for(Car car : myCars){ System.out.println(car.getCabinWidth()); } }
for ループは 3 回目の反復で失敗するため、これは公開されたデザインです。
これを修正するには、継承そのものであるルートを攻撃する必要があります。
解決策 1 : (階層の破壊)
継承を解除する必要があります。代わりに、Car と RacingCar の両方に共通の親を考え出します
Vehicle という非常に汎用的な親クラスを作成します
public class Vehicle{ public double getInteriorWidth(){ //return the interior width } }
public class Car extends Vehicle{ @Override public double getInteriorWidth(){ return this.getCabinWidth(); } public double getCabinWidth(){ //return cabin width } }
public class RacingCar extends Vehicle{ @Override public double getInteriorWidth(){ return this.getCockpitWidth(); } public double getCockpitWidth(){ //return the cockpit width of the racing car } }
public class VehicleUtils{ Vehicle vehicle1 = new Car(); Vehicle vehicle2 = new Car(); Vehicle vehicle2 = new RacingCar(); List<Vehicle> vehicles = new ArrayList<>(); vehicles.add(vehicle1); vehicles.add(vehicle2); vehicles.add(vehicle3); for(Vehicle vehicle : vehicles){ System.out.println(vehicle.getInteriorWidth()); } }
**階層の破壊: 置換が失敗した場合、階層を破壊します
解決策 2: 聞かないで伝える
Amazon の別の例を見てみましょう
Amazon では、すべてのサードパーティ製品に対して x 額の割引を提供しています。
すべての自社製品を 1.5 倍で提供します (Amazon ベーシック 製品は Amazon の自社製品です)
public class Product{ public double discount = 20;//x amount of discount on all the third-party products on Amazon public double getDiscount(){ return discount; } }
public class InHouseProduct extends Product{ public void applyDiscount(){ discount = discount*1.5;// 1.5 times more discount on InHouseProducts } }
public class PricingUtils{ Product p1 = new Product(); Product p2 = new Product(); Product p2 = new InHouseProduct(); List<Product> products = new ArrayList<>(); products.add(p1); products.add(p2); products.add(p2); for(Product product : products){ if(product instanceOf InHouseProduct){ ((InHouseProduct)product).applyDiscount(); } System.out.println(product.getDiscount()); } }
if ステートメントは、Liskov 置換原則に反します InHouseProduct の割引額の更新に関係していることに注意してください (オブジェクト Product をそのサブタイプ InHouseProduct に置き換えることができたはずです)。 if ステートメントでは、行うべきではない割引額を手動で更新しています。
InHouseProduct クラスを少し変更すると、この問題が解決されます
public class InHouseProduct extends Product{ @Override public double getDiscount(){ applyDiscount(); return discount; } public void applyDiscount(){ discount = discount*1.5; } }
そして最後に PricingUtils クラスから if ステートメントを削除します
public class PricingUtils{ Product p1 = new Product(); Product p2 = new Product(); Product p2 = new InHouseProduct(); List<Product> products = new ArrayList<>(); products.add(p1); products.add(p2); products.add(p2); for(Product product : products){ System.out.println(product.getDiscount()); } }
尋ねないでください: ここでは、utils クラスにすべての割引を出力するように指示しています。utils クラスは何も尋ねる必要はありません (if で尋ねていたため)先ほどの発言)
以上がリスコフ置換原理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。