Skip to content
Jang HyungSuk edited this page Jan 14, 2022 · 2 revisions

리스코프 치환 원칙

멤버들과 이번 미션에 대해서 간단한 리뷰를 하던 중 삼각형과 사각형은 다각형을 상속받아서 구현할 수 있지 않을까? 라는 의견을 냈다. 가만히 생각해보니 가능 할 것 같았다. 이 과정에서 예전에 공부한 정사각형 - 직사각형 예제로 리스코프 치환 원칙이 생각이 나서 한번 다시 정리해보려고 한다.


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) 을 했을 때와 기대값이 전혀 다르다. 즉 '상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.' 는 리스코프 치환 법칙을 위반하고 있다. 하위형에서 선행 조건이 강화된 예인데 이와 같이 상속을 진행하게 되면 나중에 코드의 확장이 어려워지고 기능 확장을 하기 위해서는 훨씬 더 많은 부분을 고쳐야 한다.

직사각형 is kind of 다각형 , 삼각형 is kind of 다각형

그럼 내 코드에서 직사각형과 삼각형을 다각형을 상속해서 구현한다면 리스코프 치환 법칙을 지키면서 구현을 할 수 있는 것일까? 나는 가능하다고 판단을 했다. 상위 클래스(다각형) 에서 기대되는 행위들을 하위 클래스(직사각형, 삼각형)에서 기대되는 행위가 같았기 때문이다. 따라서 아래와 같이 코드를 변경하였고 그 결과 많은 중복된 코드를 거둬낼 수 있었다.

다각형 클래스

상속 전 직사각형

상속 후 직사각형

상속 전 삼각형

상속 후 삼각형