리스코프 치환 원칙은 일반화 관계에 대한 이야기이며 자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다는 뜻이다.
LSP를 만족하면 프로그램에서 부모 클래스의 인스턴스 대신에 자식 클래스의 인스턴스로 대체해도 프로그램의 의미는 변화되지 않는다.
이를 위해 부모 클래스와 자식 클래스 사이는 행위가 일관되어야 한다.
LSP를 만족하려면 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 대신할 수 있어야 한다.
부모 클래스의 인스턴스가 실행하는 행위는 자식 클래스의 인스턴스들도 실행할 수 있어야 한다.
public class Bag {
private int price;
public void setPrice(int price) {
this.price = price;
}
public int getPrice() {
return price;
}
}
public class DiscountBag extends Bag {
private double discountedRate = 0;
public void setDiscountedRate(double discountedRate) {
this.discountedRate = discountedRate;
}
public void applyDiscount(int price) {
super.setPrice(price - (int)(discountedRate * price));
}
}
현재 Bag 클래스의 setPrice와 getPrice 메서드가 재정의되지 않았으므로 LSP를 위반하지 않는다.
그러나 다음과 같이 setPrice 메서드를 오버라이드하면 부모 클래스와 값이 달라져 LSP를 위반한다.
public class DiscountBag extends Bag {
private double discountedRate = 0;
public void setDiscountedRate(double discountedRate) {
this.discountedRate = discountedRate;
}
public void applyDiscount(int price) {
super.setPrice(price - (int)(discountedRate * price));
}
@Override
public void setPrice(int price) {
super.setPrice(price - (int)(discountedRate * price));
}
}
다음과 같이 코드를 실행하면 상위 클래스인 Bag의 setPrice를 호출할 수 없다.
Bag이냐 DiscountBag이냐에 따라서 행위가 변경되어서 리스코프 치환 원칙을 위반한다.
main {
Bag bag = new DiscountBag();
bag.setPrice(); // 하위 클래스의 setPrice 메소드 호출
}
LSP를 만족시키는 방법은 상속 관계를 제거하는 것이다.
LSP를 만족시키는 방법은 상위 클래스의 메소드를 오버라이드하지 않는 것이다.