티스토리 뷰

728x90
반응형

리스코프 치환 원칙(Liskov substitution principle, LSP)

Effective Java 를 보다가 접했는데, 검색해보니 객체지향 5대 원칙(SOLID) 중 하나라고 한다.

이것도 나중에 정리하기 위해 참고 링크를 걸어두어야 겠다.

모든 개발자가 알아야만 하는 SOLID 원칙

  1. 리스코프 치환 원칙 ?

  2. 리스코프 치환 원칙 예제

  3. References

리스코프 치환 원칙 ?

상위 타입의 객체를 하위 타입의 객체로 치환해도 상위 타입을 사용하는 프로그램은 정상적으로 동작해야 한다.

리스코프 치환 원칙은 기능의 명세(계약)에 대한 내용이다.

기능 실행의 계약과 관련해서 흔히 발생하는 위반 사례는 다음과 같다.

  1. 명시된 명세에서 벗어난 값을 리턴한다.

  2. 명시된 명세에서 벗어난 예외를 발생시킨다.

  3. 명시된 명세에서 벗어난 기능을 수행한다.

말이 어렵지만 쉽게 말하자면 상위 타입에서 지정한 값, 예외, 기능 외에 다른 행동을 한다면

코드가 비정상적으로 동작할 수 있다는 말이다.

리스코프 치환 원칙 예제

사각형을 부모로 갖는 정사각형 클래스 구현.

높이와 너비를 가지는 Rectangle 클래스이다.

 class Rectangle {
     protected double width;
     protected double height;
 
     public double getWidth() {
         return width;
    }
 
     public void setWidth(double width) {
         this.width = width;
    }
 
     public double getHeight() {
         return height;
    }
 
     public void setHeight(double height) {
         this.height = height;
    }
 
     public double getArea() {
         return this.getWidth() * this.getHeight();
    }
 }

그럼 이제 Rectangle 을 상속하는 Square 클래스를 구현해 보자.

정사각형이기 때문에 setHeight(), setWidth()Override 하여, width, height 을 같은 값으로 초기화한다.

 class Square extends Rectangle {
     @Override
     public void setWidth(double width) {
         this.width = width;
         this.height = width;
    }
 
     @Override
     public void setHeight(double height) {
         this.height = height;
         this.width = height;
    }
 }

얼핏 보아서는 사각형을 부모로 갖는 정사각형이라는 객체로, 객체 지향적으로 구현된 것처럼 보인다.

하지만 테스트 코드를 보자.

 class DoWork {
     public boolean work(Rectangle rectangle) {
         rectangle.setHeight(5);
         rectangle.setWidth(4);
 
         return rectangle.getArea() == 20;
    }
 }
 
 public class LSP_Test {
     public static void main(String[] args) {
         DoWork work = new DoWork();
         System.out.println(work.work(new Rectangle())); // true
         System.out.println(work.work(new Square())); // false
    }
 }
 

work() 는 상위 타입(Rectangle) 을 사용하는 객체이다.

리스코프 치환 원칙에 따르면 RectangleSquare 객체로 치환하여도 프로그램은 정상적으로 동작하여야 한다.

하지만 결과를 보면 Square 객체를 전달했을 때는 false 를 반환 하는 것을 볼 수 있다.

자식 클래스인 Square 가 부모 클래스인 RectanglegetArea() 의 기능을 제대로 수행하지 못 하고 있기 때문이다.

이러한 경우, '리스코프 치환 원칙을 위반했다' 고 한다.

그렇다면 정상동작을 위해서 Square 클래스에 getArea()Override 하여 Rectangle 과 다른 동작을 하면 될 것인가 ?

현재 구현 상황에서 보자면, 그렇게 할 경우 Rectangle 의 모든 기능을 새로 수행하는 것으로 보인다.

' 그렇다면 정말 Square 클래스가 Rectangle 의 자식이 맞는 것인가? '

라는 의심이 든다.

어느 방향으로든 정상동작하도록 코드를 수정할 수 있겠지만 중요한 개념은,

자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 한다.

References

리스코프 치환 원칙

LSP(Liskov Substitution Principle)이란?


반응형
공지사항
최근에 올라온 글