파생 클래스는 기본 클래스를 대체할 수 있어야 한다.
- 로버트 C. 마틴
바바라 리스코프에 의해 고안된 ‘기본 클래스의 계약을 파생 클래스가 제대로 치환할 수 있는지 확인하라’는 원칙이다. 리스코프 원칙을 위배하는 코드로 원칙에 대해 알아볼 것이다.
class Rectangle{
protected long width;
protected long height;
public long culculateArea(){
return width*height;
}
}
class Square extends Rectangle{
public Square(long length){
super(length, length);
}
}
Rectangle rectangle = new Square(10);
rectangle.setHeigth(5);
System.out.println(rectangle.calculateArea());
이 코드의 출력 결과는 50일 것이다. 하지만 정사각형의 높이와 길이가 다를 수 없기 때문에 의도된 결과는 아니다. 이는 대표적인 리스코브 치환 원칙의 위반 사례이며, 이 원칙에 의하면 파생 클래스가 기본 클래스의 모든 동작을 대체할 수 있어야한다. 파생 클래스가 기본 클래스의 모든 동작을 대체 가능한지 파악하기 위해서는 기본 클래스에 할당된 의도가 무엇인지 알아야한다.
기본 클래스는 set을 통해 높이와 길이에 대해 값을 할당하고, 메서드로 넓이를 계산한다. 이 동작대로 우리가 정사각형에게 기대하는 동작을 수행하도록 코드를 변경해보자
class Square extends Rectangle{
public Square(long length){
super(length, length);
}
@Override
public void setWidth(long width){
super.width = width;
super.height = width;
}
@Override
public void setHeight(long width){
super.width = height;
super.height = height;
}
}
Setter를 통해 높이와 길이를 변경해도 같은 변의 길이를 가지도록 코드를 만들었다. 하지만 이는 기본 클래스의 의도를 위반하였다. 기본 클래스는 직사각형이기 때문에 높이를 변경 했을 때 길이가 변경되는 것은 의도가 아닐 것이다. 결국 이 코드에서 온전히 리스코프 원칙을 지키기는 어렵다.
기본 클래스의 의도를 파악하기 위해서는 초기 코드 작성자의 의도를 알아야 하는데, 코드만 보고는 의도를 파악하기란 어렵다. 이를 위한 몇 가지 방법이 있다.