본문 바로가기

Design/Architecture

[Clean Architecture]SOLID 원칙 : 3.LSP 리스코프 치환 원칙

LSP 란?

바바라 리스코프라는 사람이 정의한 하위 타입에 대해 먼저 생각해보자

.

여기에서 필요한 것은 다음과 같은 치환 원칙이다. S 타입의 객체 o1 각각에 대응하는 T 타입 객체 o2가 있고,
T타입을 이용해서 정의한 모든 프로그램 P에서 o2자리에 o1을 치환하더라도 P의 행위가 변하지 않는다면,

S는 T의 하위 타입이다.

 

 

말이 쪼금 헷갈리니... 상황을 다시 적어본다면,, 어떤 프로그램 P가 있는데 이게 T타입을 이용해서 만들었고, 만약에 이 T 타입의 객체 o2자리에 S 타입의 객체 o1로 치환해도 P행위가 변하지 않는다면 S는 T의 하위 타입이다... 즉 S는 T를 상속하였고 그 기능들을 다 가지고 있을 것이고, 따라서 S든 T든 P행위에도 영향을 끼치지 않았겠지? 무튼 LSP 원칙이 이러한 상황과 관련이 있다는 것이다.

 

LSP를 위반하는 문제

책에서 나온 정사각형/직사각형 문제를 살펴보자.

 

[Clean Architecture] Square/Rectangle 문제

위 표를 보게되면 Square(정사각형) 객체가 Rectangle(직사각형) 객체를 상속 받고있다. 만약 User가 Rectangle 대신 Square로 치환하였을 때, User의 행위에 영향을 끼치지 않는다면 LSP 원칙이 잘 적용된 케이스라고 할 수 있을 것이다.

 

하지만 이 케이스는 LSP법칙을 위반하고 있다. 직사각형의 경우 밑변과 높이는 독립된 변수이다. 하지만 정사각형의 경우는 밑변과 높이가 서로 종속되어있다.(밑변이 바뀌면 높이도 바뀜) 따라서 User에서 만약 직사각형의 밑변과 높이를 세팅하고 면적을 구하는 로직을 돌렸다고 했을 때, 이를 정사각형 부분으로 바꾼다면 다른 행동을 하게 될 것이라는 것이다!

Rectangle r = ...
r.setW(5);
r.setH(2);
assert(r.area() ==10);

 이때 LSP를 위반하지 않기 위해서 type 체크를 통해서 if문으로 분기를 하는 방법이 있을텐데, 이 방법은 결국 User가 타입에 종속되는 것이므로 해결책이 되지 못한다고 한다...(모오든 케이스를 다 적을 수 없잖아..)

 

 

LSP와 아키텍처

LSP는 처음에는 상속 가이드 정도 였다고하나 시간이 지나면서 인터페이스와 구현체에도 적용되는 더 범위가 넓어진 SW설계 원칙으로 바뀌게 되었다.

 

나도 LSP를 공부하면서 떠오른 것이 JAVA의 interface였다. 이를 통해서 실제 객체의 Class type을 체크하지 않아도 프로그램의 행동에 문제가 없이 돌아가도록 설계를 한다. (해당 인터페이스를 구현한 객체라면 어떤 것이든 상관없이..)

 

그리고 더 큰 범위에서는 REST API 방식의 서비스를 생각할 수도 있을 것이다. (택시파견서비스 LSP위배 사례를 책에서 참고해볼 것)

 

LSP라는 것이 잘 지켜진 시스템이라면, 더 성능이 뛰어나고 좋은 모듈이 개발되었다고할 때, 그 모듈로 간편하게 교체(치환)하는 것이 가능해질 것이고 이는 아마 확장가능성 면에서도 큰 도움이 되겠다 라는 생각까지 확장이 될 수 있을 듯하다.

 

결론

LSP를 위반한다면 별도의 메커니즘의 추가로 아키텍처의 오염을 피할 수 없을 것이다. 

따라서 LSP는 아키텍처 수준까지 확장해서 생각을 해야만 함을 인지하도록 하자!

이번 LSP를 통해서 2편 OCP에서 등장했던 복잡한 클래스 구성도를 안 떠올릴 수가 없었다. 아마 그 예시에서 치환이라는 의미가 어느정도 와닿았기를 바란다.