Liskov Substitution Principle: Improves the quality of your code

Yan Yan
3 min readJul 28, 2022

--

code ရေးတဲ့ အချိန်မှာ senior တစ်ယောက်ယောက်ကနေ မင်း code တွေက logically အရ မှန်လား ၊ robust ရောဖြစ်ရဲ့လား မေးနိုင်တယ်၊ ဒါတွေကို ဖြေရှင်းဖို့ software principle တွေကို သိထားမှ အဆင်ပြေမယ်၊ ဒီ article မှာ SOLID ရဲ့ Liskov Substitution Principle ကို ဖော်ပြသွားပါမယ် အတိုကောက် LSP လို့ခေါ်ကြတယ်။

What is Liskov Substitution Principle? and What does it do?

LSP ရဲ့ အကျဉ်းချုပ်ကို ဆိုရရင်

Objects should be replaceable with their subtypes without affecting the correctness of the program.

Object တစ်ခုကို သူ့ရဲ့ subtypes တွေနဲ့ အစားထိုးနိုင်ရမယ်။ example နဲ့ ကြည့်ရအောင်

sub-type from parent type

HatchBack က car အမျိုးအစားဖြစ်တယ်၊ Ostrich က Bird ၊ Gasoline က fuel ဖြစ်တယ်။ code အနေနဲ့ ရေးရင်

class Car {

}

class Hatchback extends Car {

}

class Bird {

}

class Ostrich extends Bird {

}

class Fuel {

}

class Gasoline extends Fuel {

}

LSP အရဆို car ကို Hatchback ဖြင့် အစားထိုးနိုင်သည်။ ထို့ကြောင့် bird ကို Ostrich နှင့် fuel ကို Gasoline ဖြင့် အစားထိုးနိုင်သည်။ ဒါဆိုရင် အဆင်ပြေတယ်။ LSP ကို ချိုးဖောက်တဲ့ code ကို ဘယ်လိုပြင်မလဲ example နဲ့ ကြည့်ရအောင်

[1] Violation of LSP Example 1, Fix by Breaking The Hierarchy

Break the hierarchy if it fails the substitution test.

(1) car အားလုံးမှာ ကားအတွင်းခန်း( car cabin) ရှိလား ကြည့်ရအောင်

racing car doesn’t have cabin

Racing car မှာ ကားအိမ်မရှိဘူး၊ အော်က code ကိုကြည့်

public class Car {public double getCabinWidth() {
// return cabin width
}
}public class RacingCar extends Car {@Override
public double getCabinWidth() {
//UNIMPLEMENTED!
}

public double getCockpitWidth() {
// return cockpit width
}
}

car cabin မရှိတဲ့အတွက်ကြောင့် unimplemented ဖြစ်နေတယ်။ ဒီ code က software design အရ မှားနေတယ်လို့ ပြောရမယ် ဘယ်လိုပြန်ပြင်မလဲ

(2) Break the hierarchy

public class Vehicle {

public double getInteriorWidth() {
// return 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 getCabinWidth() {
return this.getCockpitWidth();
}

public double getCockpitWidth() {
// return cockpit width
}

}

Car ကို inheriting လုပ်မဲ့အစား Vehicle class ကို create လုပ်ပီး inheriting လုပ်ပီးဖြေရှင်းလိုက်တယ်၊ ဒီလိုဆို LSP principle အတိုင်းဖြစ်သွားမယ် code designအရ ပိုကောင်းသွားမယ်၊

[2] Violation of LSP Example 2

Object ကို cast လုပ်စရာမလိုဘဲ အစားထိုးနိုင်ရမယ် “ Tell, Don’t ask ”

(1) Bad Practice. Casting

public class Product {

protected double discount = 20;

public double getDiscount() {
return discount;
}

}

public class InHouseProduct extends Product {

public void applyExtraDiscount() {
discount = discount * 1.5; // multiplies 1.5 times
}

}public class PrincingUtils {

public static void main(String[] args) {

Product p1 = new Product();
Product p2 = new Product();
Product p3 = new InHouseProduct();

List<Product> productList = new ArrayList<>();

productList.add(p1);
productList.add(p2);
productList.add(p3);

for(Product product : productList) {
if (product instanceof InHouseProduct) {
// Type casting
// Not Correct Way
((InHouseProdcut) product).applyExtraDiscount();
}

System.out.println(product.getDiscount()); }
}

}

ဒီ code မှာ ဘယ်အပိုင်းက problem ရှိနေလဲဆိုရင်

if (product instanceof InHouseProduct) {
// Type casting
// Not Correct Way
((InHouseProdcut) product).applyExtraDiscount()
}

LSP အရဆို code က မှားနေတယ်၊ rule အရ Object တစ်ခုက သူ့ကို sub types အစားထိုးနိုင်ရမယ် — “Objects should be replaceable with their subtypes without affecting the correctness of the program.”

ဒီ code မှာ sub type အစားထိုးတဲ့နေရာမှာ type cast or asking ပြန်လုပ်နေရတယ်။

(2) Tell, Don’t ask

public class Product {

protected double discount = 20;

public double getDiscount() {
return discount;
}

}

public class InHouseProduct extends Product {

@Override
public double getDiscount() {
this.applyExtraDiscount();
return discount;
}

public void applyExtraDiscount() {
discount = discount * 1.5; // multiplies 1.5 times
}

}
public class PricingUtils {

public static void main(String[] args) {

Product p1 = new Product();
Product p2 = new Product();
Product p3 = new InHouseProduct();

List<Product> productList = new ArrayList<>();

productList.add(p1);
productList.add(p2);
productList.add(p3);

for(Product product : productList) {
System.out.println(product.getDiscount());
}

}

}

InHouseProduct class မှာ getDiscount() method ကို overide လုပ်လိုက်မယ်၊ သူ့ရဲ့ function ကို redefine ပြန်လုပ်လိုက်တယ်။

အကျိုးနေနဲ့ PricingUtil class မှာ sub class က type ကို cast လုပ်စရာမလိုဘဲ loop ပတ်လို့ရသွားမယ် ၊ ဒါကြောင့် code ကို “Tell, Don’t ask’’ ဖြစ်အောင်ရေးပါ။

Conclusion

Robust software ဖြစ်အောင် ရေးဖို့ design ချတဲ့ခါ LSP ကိုလိုက်နာရပါမယ်။ class hierarchy ကိုကြည့်ပီး ဘယ်ကောင်တွေ logically အရ failed ဖြစ်နေလဲဆိုတာကို ရှာပီး testing လုပ် ပီးရင် refactoring ပြန်လုပ် ဒီလို ထပ်ခါထပ်ခါ လုပ်ရင်း ပိုနားလည်လာမယ်။ logically အရမှန်ရင် code လည်း တိကျမှုရှိလာပါမယ်။

‘intentional practices’ များများလုပ်ဖို့တော့ လိုပါမယ်။

--

--