Day09
멤버들과 이번 미션에 대해서 간단한 리뷰를 하던 중 후
가 삼각형과 사각형은 다각형을 상속받아서 구현할 수 있지 않을까?
라는 의견을 냈다. 가만히 생각해보니 가능 할 것 같았다. 이 과정에서 예전에 공부한 정사각형 - 직사각형 예제로 리스코프 치환 원칙이 생각이 나서 한번 다시 정리해보려고 한다.
is kind of
혹은 is a
관계가 성립하면 상속을 할 수 있다고 알고 있다.
정사각형 is a 직사각형
, 정사각형 is kind of 직사각형
삼각형 is a 다각형
, 삼각형 is kind of 다각형
직사각형 is a 다각형
, 직사각형 is kind of 다각형
모두 다 일반적으로 생각했을 때 맞는 말이다. 즉 상속이 가능해 보인다.
리스코프 치환 원칙
- 상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.
- 상위 클래스의 행동 규약을 하위 클래스가 위반하면 안 된다.
일반적으로 리스코프 치환 원칙을 지키면서 상속을 하면 문제가 없지만 리스코프 치환 원칙을 위반하게 된다면 잘못된 상속을 하게 된다.
하위형(하위 클래스)이 만족해야 하는 조건 3가지
- 하위형에서 선행 조건은 강화될 수 없다.
- 하위형에서 후행 조건은 약화될 수 없다.
- 하위형에서 상위형의 불변 조건은 반드시 유지되어야 한다.
-> 그냥 간단히 말하자면 하위 클래스에서 오버라이딩을 할 때 상위 클래스에 있는 형식과 일치하도록 오버라이딩을 해야 한다는 것이다. 간단하게 상위 클래스에서 사용할 때와 같이 동작하도록 오버라이딩을 진행하면 리스코프 치환 원칙을 만족시킬 수 있다.
가장 유명한 리스코프 치환 원칙을 위반한 예를 보자 (직사각형, 정사각형 예제)
public class Rectangle {
private int width;
private int height;
public Rectangle(int width, int height) {
this.width = width;
this.height = height;
}
public int getArea() {
return width * height;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public void increaseWidth(int num) {
this.width += num;
}
public void increaseHeight(int num) {
this.height += num;
]
}
public class Square extends Rectangle {
public Square(int length) {
super(length, length);
}
@Override
public void increaseWidth(int num) {
super.increaseWidth(num);
super.increaseHeight(num);
}
@Override
public void increaseHeight(int num) {
super.increaseWidth(num);
super.increaseHeight(num);
}
}
Rectangle rec = new Square(10);
System.out.println(rec.getWidth());
System.out.println(rec.getHeight());
rec.increaseWidth(10);
System.out.println(rec.getWidth());
System.out.println(rec.getHeight());
///
10
10
20
20
위의 코드는 Rectangle rec = new Rectangle(10, 10)
로 선언했을 때와 다르게 동작한다. 상위 클래스에서 increaseWidth(10)
을 했을 때와 기대값이 전혀 다르다. 즉 '상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.' 는 리스코프 치환 법칙을 위반하고 있다. 하위형에서 선행 조건이 강화된 예인데 이와 같이 상속을 진행하게 되면 나중에 코드의 확장이 어려워지고 기능 확장을 하기 위해서는 훨씬 더 많은 부분을 고쳐야 한다.
그럼 내 코드에서 직사각형과 삼각형을 다각형을 상속해서 구현한다면 리스코프 치환 법칙을 지키면서 구현을 할 수 있는 것일까? 나는 가능하다고 판단을 했다. 상위 클래스(다각형) 에서 기대되는 행위들을 하위 클래스(직사각형, 삼각형)에서 기대되는 행위가 같았기 때문이다. 따라서 아래와 같이 코드를 변경하였고 그 결과 많은 중복된 코드를 거둬낼 수 있었다.
다각형 클래스
상속 전 직사각형
상속 후 직사각형
상속 전 삼각형
상속 후 삼각형