04. Liskov Substitution Principle

Wonseok Lee·2021년 12월 14일
0

Design Patterns

목록 보기
4/6
post-thumbnail

Liskov Substitution Principle

한 줄 요약하면, 코드에서 super class의 object가 있을 자리에 derived class의 object를 가져다 놓아도 문제가 없어야 한다.이다.

상술한 문장 그 자체로 이해가 쉽지만, 다른 원칙들에 비해 비교적 formal하게 rule이 정의가 되어있으므로 지난하더라도 하나하나 살펴보도록 하자.

참고로, 후술에서 표현의 간결함을 위해 아래와 같이 용어를 사용하도록 하겠다.

  • super method: super class의 method
  • derived method: derived class의 method
  • super_obj: super class의 instance
  • derived_obj: derived class의 instance

7 Rules for Liskov Substitution

  • Rule 1) derived method의 parameter type은 super method와 정확하게 일치하거나, 더 추상화 되어 있어야 한다.
    • 조금만 생각해보면 지당하기 그지 없는 말씀이다.
    • super method가 feed(Cat c)과 같이 정의되어 있고, 코드의 어느 곳엔가 super_obj.feed(cat)과 같은 코드가 존재한다고 해보자.
    • Rule 1은 super_obj.feed(cat)derived_obj.feed(cat)와 같이 치환했을 때, 문제 없이 동작하도록 만들라는 것이다.
    • 이러려면 당연히, derived_obj.feed method의 parameter type은 똑같이 Cat을 인자로 받거나(i.e. feed(Cat c)) 더 추상화된 object를 인자로 받아야 한다(i.e. feed(Animal a)).
  • Rule 2) derived method의 return type은 super method와 정확하게 일치하거나, 덜 추상화 되어 있어야 한다.
    • Rule 1의 역산(?)에 대응되는 말로, 역시 생각해보면 지극히 지당한 말씀이다.
    • super method가 Cat GetOldest()과 같이 정의되어 있고, 코드의 어느 곳엔가 Cat oldest_cat = super_obj.GetOldest()와 같은 코드가 존재한다고 해보자.
    • super_objderived_obj로 치환해도 문제가 없으려면 derived method는 정확히 일치하는 return type을 갖거나(i.e. Cat GetOldest()) 보다 덜 추상화가 된 return type을 가져야 한다(i.e. BengalCat GetOldest()).
  • Rule 3) derived method는 super method가 throw하지 않을 exception을 throw하면 안된다.
    • 큰 설명이 필요 없을 정도로 간단하다.
    • 만약, derived method가 super method가 throw하지 않던(따라서, 이를 catch하는 구현이 되어 있지 않은 상태) exception을 throw하면, 이는 handling되지 못하고 Software의 중단을 야기한다.
    • 따라서, derived method는 super method가 throw하는 exception만을 throw하여야 한다.
  • Rule 4) derived method는 super method의 pre-condition을 강화하면 안된다.
    • 코드 내에 존재하는 super_objderived_obj로 치환했을 때의 문제점을 떠올려보면 이해가 편하다.
    • 예를 들어 DoSomething(int x)와 같은 양수/음수를 모두 받을 수 있는 method를 derived method에서 음수를 받으면 exception을 throw하는 식으로 상속하지 말라는 이야기이다.
    • formal한 정의이기에 별도의 Rule로 정리하지만 사실 다른 Rule들과 그닥 orthogonal하게 정돈되지는 않는 듯 하다.
  • Rule 5) derived method는 super method의 post-condition을 약화하면 안된다.

    • super_obj.DoSomething()을 호출한 후의 상황과 derived_obj.DoSomething()을 호출한 후의 상황을 동일하게 만들어주어야 한다는 이야기이다.
    • 만약, super_obj.DoSomething()가 모든 TCP/IP connection을 disconnect하는 일을 수행한다고 해보자.
    • 그렇다면, 이를 overriding한 derived_obj.DoSomething() 역시 모든 TCP/IP connection을 끊어야지, 뭔가를 남겨두면 안된다.
  • Rule 6) derived method는 super method의 invariant를 보존해야 한다.

    • super_obj.DoSomething()을 호출한 후에 바뀌지 않는 것이 보장되었던 것들을 derived_obj.DoSomething()을 호출 후에도 보장해주어야 한다는 이야기이다.
    • formal한 정의이기에 별도의 Rule로 정의하지만 사실 Rule 4/5와 큰 차이가 없지 않나 싶다.
  • Rule 7) derived method는 super method의 private field를 변경해서는 안된다.

    • Rule 7은 매우 명시적인 규칙인데, C++을 사용한다면 syntax적으로 금지되어 있어 크게 고려할 것이 없다.
profile
Pseudo-worker

0개의 댓글