[SOLID] 리스코프 치환 원칙

이재훈·2023년 8월 15일
0

SOLID

목록 보기
3/5

객체지향의 핵심원리와 원칙들

LSP, 리스코프 치환 원칙

LSP : Liskov Substitution Principle
부모 클래스가 할 수 있는 행동은 자식 클래스도 할 수 있어야 한다.

원래의 정의는 이것이 아니나 직관정의는 이렇다 할 수 있습니다.

리스코프 치환 원칙이 깨진 상황과 그 문제점

부모 클래스가 할 수 있는 걸 자식이 할 수 없는 상황

public class Parent {
    public void someMethod(int input) {
        System.out.println("Parent 정상적으로 호출 됨");
    }
}
public class Child extends Parent{
    @Override
    public void someMethod(int input) {
        if (input <= 0) {
            throw new RuntimeException("양수만 받을 수 있습니다.");
        }

        System.out.println("Child 정상적으로 호출됨");
    }
}

Child는 Parent를 상속 받아 someMethod를 오버라이딩하고 있습니다. 부모 클래스인 parent 클래스에는 어떤값도 다 받을 수 있지만 자식 클래스인 Chil 클래스는 양수만 받을 수 있습니다. "리스코프 치환 원칙"이 깨진 상황입니다.

이게 무엇이 문제일까요? 이것을 사용하는 Client를 확인해보도록 하겠습니다.

public class Client {
    public void someClientMethod(Parent parentOrChild) {
        parentOrChild.someMethod(-1);
    }
}
public class LspExample {
    public static void main(String[] args) {
        Client client = new Client();

        Child child = new Child();

        client.someClientMethod(child);
    }
}

Client 입장에서는 parentOrChild에 Parent가 들어있는지 Child가 들어있는지 모릅니다. 이 코드는 당연히 RuntimeException이 발생하게 됩니다. 이 문제를 어떻게 해결할 수 있을까요?

  1. Child 대신 Parent 인스턴스를 넣어준다.
  2. 파라미터로 양수만 넣어준다.
  3. instanceof로 어떤 인스턴스인지 구분하여 호출한다.

Child 대신 Parent 인스턴스를 넣어준다.

이 방법은 Parent만 사용하게 되기 때문에 다형성이 없어집니다.

파라미터로 양수만 넣어준다.

Parent 클래스는 모든 값을 파라미터로 받을 수 있기 때문에 원래의 기능이 제한되게 됩니다.

instanceof로 어떤 인스턴스인지 구분하여 호출한다.

public class Client {
    public void someClientMethod(Parent parentOrChild) {
        
        if (parentOrChild instanceof Parent && false == parentOrChild instanceof Child) {
            parentOrChild.someMethod(-1);
        } else if (parentOrChild instanceof Child) {
            parentOrChild.someMethod(Math.abs(-1));
        }
        
    }
}

이런식으로 코드를 작성하게 되면 우리가 생각한대로 동작하긴 하겠지만 이것은 다형성이 깨진 코드가 됩니다. Parent와 Child 외에 상속 받은 클래스가 늘어나게 되면 Client 코드에 if문이 추가되게 될 것입니다.

어떻게 해야 했는가?

답은 애초에 상속 관계를 가지지 말았어야 합니다. 혹은 반대로 Child가 부모클래스, Parent가 자식 클래스가 되어야 했습니다. 상속을 할 때 리스코프 치환원칙을 지키지 않으면 다형성을 깨트린 코드가 됩니다.

계약에 의한 설계

상속에서 이루어져야하는 몇가지 조건에 대해 담고 있습니다. 그 중 하나는 아래와 같습니다.

사전 조건은 자식 클래스에서 더 강해지면 안된다.

자식 클래스가 더 엄격하면 안된다. 이해해도 될 것 같습니다.

위의 예시중 하나는 가능하고 하나는 불가능합니다. 어떤것이 가능할까요?
바로 아래의 예시가 가능합니다. 부모 클래스에서 public이던 메서드는 자식클래스에서 private 하게 바꾼다면 이것은 더 엄격하게 바뀌는 것입니다.

아래 예시처럼 private 한 것을 public으로 바꾸는 것만이 가능합니다.

리스코프 치환 원칙의 원래 정의

리스코프 치환원칙의 원래 정의는 아래와 같습니다.

파생 클래스는 기반 클래스를 대체할 수 있어야 한다.

여기서 기반 클래스는 부모클래스, 파생 클래스는 자식 클래스입니다. 바로 자식 클래스가 부모 클래스를 대체할 수 있어야 한다는 것입니다. 부모 클래스가 할 수 있을 것으로 기대되는 여러가지 행동들은 자식 클래스도 가능해야한다는 점입니다.

결론

리스코프 치환 원칙을 지키는 코드는 진정한 의미에서 다형성을 제공해주기 때문에 코드의 분기 없이도 올바르게 확장 가능한 코드를 만듭니다.


해당 게시글은 프로그래머스 스쿨 강의
"실무 자바 개발을 위한 OOP와 핵심 디자인 패턴(푸)"
를 정리한 내용입니다. 쉽게 잘 설명해주시니 여러분도 강의를 듣는 것을 추천드립니다.

profile
부족함을 인정하고 노력하자

0개의 댓글