아래 코드를 살펴보자.
class Rectangle {
int width;
int height;
public void setHeight(int value) {
this.height = value;
}
}
class Square extends Rectangle {
@Override
public void setHeight(int value) {
this.height = value;
this.width = value;
}
}
사각형 Rectangle이 있고, 정사각형 Square는 Rectangle을 상속받는다.
height를 바꾸려고 하면, 정사각형의 특성 상 height와 width를 모두 변경한다.
위 예제는 LSP를 위배할까?
정답은 "위배한다" 이다.
먼저 LSP(리스코프 치환 원칙)란, 다음과 같다.
하위 클래스는 상위 클래스의 행위를 깨뜨리지 않으면서 상위 클래스를 대체할 수 있어야 한다.
위배되는 이유는 다음과 같다.
Rectangle에는 width, height를 독립적으로 설정할 수 있다.
하지만 Square는 height를 설정할 때 width도 동시에 변경된다.
Rectangle rect = new Square();
rect.setHeight(10);
System.out.println(rect.width); // 예상: 기본값(변경 X), 실제: 10
이 경우 Rectangle의 기본 가정인 "width, height를 독립적으로 설정할 수 있다."는 가정이 깨진다.
즉, Rectangle을 사용하는 코드에서 Square로 대체했을 때 예측하지 못한 동작이 발생할 수 있다.
public void resizeRectangle(Rectangle rect) {
rect.setHeight(10);
rect.setWidth(20);
assertThat(rect.width * rect.height).isEqualTo(200); // Error
}
여기서는 setHeight(10), setWidth(20)을 적용하면 넓이가 200 이상이 될 것이라고 가정한다.
하지만 rect가 Square라면 setWidth(20) 호출 시 height도 20이 되어 예상과 다르게 동작한다.
인간이 개념적으로 이해하기로는 정사각형이 사각형의 한 종류라고 볼 수 있지만, 사실 Rectangle과 Square는 본질적으로 다르게 동작한다.
따라서 상속 관계를 제거하고 별개의 클래스로 만드는 것이 좋다.
class Rectangle {
private int width;
private int height;
public int getArea() {
return width * height;
}
}
class Square {
private int side;
public int getArea() {
return side * side;
}
}