[Spring] 객체 지향

노유성·2023년 7월 11일
0
post-thumbnail

다형성

다형성을 실세계와 비유를 하기 위해 세상을 2가지의 개념으로 바라보자. 바로 역할과 책임이다.


자동차를 운전하는 운전자는 운전을 한다는 운전자의 역할이 있고, 자동차는 운전자의 핸들링 및 악셀을 밟는 행위에 대해서 방향을 전환하고 자동차를 앞으로 움직여야 하는 자동차의 역할이 있다. 그리고 그 역할을 구현한 것이 운전자는 면허증을 가진 사람들, 자동차의 입장에서는 여러 종류의 자동차가 될 것이다.

여기서 운전자(Client)의 입장에서 자동차가 바뀐다면 어떻게 될까? 운전자는 아반떼를 몰다가 테슬라를 몰면은 좀 더 다른 기능을 공부해야할까? 아니다. 아무것도 공부하지 않아도 된다. 이러한 것을 가능하게 한 이유가 자동차는 아무리 더 추가적인 기능이 있다고 하더라도 결국 자동차의 역할을 전부 구현한 구현체이기 때문이다.

다른 비유를 들어보자.

위는 공연을 위한 배우들의 역할과 그 구현에 대해서 그림으로 나타낸 것이다. 로미오의 역할을 줄리엣의 역할에 의존(로미오의 행동에 줄리엣의 행동이 영향을 준다.)하고 있다. 이 상황에서 줄리엣의 역할이 변경되면 어떨까? 로미오는 줄리엣의 행동에 의존하고 있기 때문에 로미오의 역할에도 변화가 생길 것이다. 이는 좋은 객체 지향 프로그래밍이 아니다.

하지만 위와 같이 역할 자체는 변하지 않는다면 우리는 역할을 소화할 수 있는 다양한 구현체들로 역할에 대한 구현체를 갈아끼울 수 있고 줄리엣의 구현이 바뀐다고해서 로미오의 역할에 영향을 주지 않게 된다. 다형성이란 이런 것이다.

마지막 예시로

MemberService(Client)는 MemberRepository에 의존하고 있으며 MemberRepository는 역할이다. client는 역할에 의존하고 있기 때문에 구현체가 바뀌어도 영향을 받지 않고 또 구현체가 바뀐다고해서 client가 그에 영향을 받지도 않는다.

한 줄로 정리하자면, 다형성의 본질은 클라이언트를 변경하지 않고 구현체를 유연하게 변경할 수 있다는 점에 있다.

더 나아가서 아래와 같은 상황들이 주어진다면?

아주 큰일이 날 것이다. 그러므로 인터페이스가 변하지 않게(안정적으로) 설계하는 것이 매우 중요하다.

SOLID 원칙

SPR(단일 책임 원칙)

하나의 클래스는 하나의 책임만을 가져야 한다는 원칙이다. 예를 들어서 클라이언트에게 데이터를 받아서 데이터를 가공한 후 db에 저장한다는 프로세스를 가진 서버가 있다고 가정하자. 그러면 서버는 3가지의 역할로 구분할 수 있다.

  1. 어떤 데이터인지 구분하는 역할
  2. 데이터에 따라 가공하는 역할
  3. 가공된 데이터를 저장하는 역할

만약에 데이터가 변경이 된다면 2번 역할을 바꾸면 될 것이고 db가 변경이 된다면 3번 역할을 바꾸면 될 것이다. 각각의 class는 하나의 책임만 갖고 있기 때문에 유지보수가 원할하다. 하지만 이 모든 기능이 하나의 클래스에 구현이 되어 있다면 그 안에서 어떤 코드를 어떻게 바꿔야할지 찾고 의사결정을 하는 데에 한세월이 걸릴 것이다.

OCP(개방, 폐쇄 원칙)

소프트웨어의 요소는 확장에는 열려 있으나, 변경에는 닫혀 있어야 한다는 것이다. 무엇인가를 기능을 추가, 확장하려면 당연히 변경이 불가피한데 이게 무슨 말일까?

여기서 변경에 닫혀있어야 한다는 점에서 변경은 client 입장에서의 변경을 얘기하는 것이다.
위 그림처럼 줄리엣 역할에 대해서 김태희, 송혜교 말고도 수십, 수백명의 배우들로 확장이 가능하다. 또 그로 인해서 로미오에 역할에 변경이 생기지 않는다. 위 그림같은 경우는 OCP 원칙이 잘 지켜지고 있는 것이다.


하지만 위 그림을 보면은 MemberService는 client임에도 불구하고 의존하고 있는 역할의 구현체가 변경을 하려니까 client 코드에 변경이 있다(주석 처리하고 추가 코드를 작성함). 이러면 OCP원칙이 잘 지켜지지 않는 것이다. 더하여 현재 client는 역할만 알고 있는 것이 아니라 구현체도 알고 있는 상황이다. 그러기에 구현체가 변경이 되면 client 코드에 변경이 생기는 것이다.

spring은 이와 같은 문제를 configuration을 이용해 해결해준다. spring은 spring bean에 등록되어 있는 class들을 관리하면서 OCP 원칙을 지켜준다.

LSP(리스코프 치환 원칙)

이 부분은 간단하다. 역할에 대해서 구현을 할 때 역할에 맞게 구현이 잘 되어야 한다는 것이다.

가령, 자동차의 악셀을 구현하면은 악셀은 앞으로 가야하는데 악셀을 뒤로가게 구현을 해버리면 LSP 원칙을 위반하는 것이다. interface에는 특정 기능에 대해서 기대하는 역할이 있는데 그 역할에 따라서 악셀이면 느리더라도 앞으로는 가게 구현을 해놓아야 한다.

ISP(인터페이스 분리 원칙)

자동차를 에시로 자동차는 자동차 내부에 운전을 하는 기능도 있지만 앞판을 열면은 정비를 하는 기능도 있다. 이렇게 세부적으로 자동차라는 인터페이스는 2개의 하위 역할로 구분이 가능하다. 이런 경우에는 인터페이스를 분리해야 한다는 것이 ISP 원칙이다.

ISP원칙에는 장점이 하나 있는데 인터페이스를 분리하면 그에 대해 client도 분리를 할 수 있다. 위 예시처럼 분리를 하면 운전자 클라이언트, 정비사 클라이언트로 분리할 수 있다.

DIP(의존관계 역적 원칙)

이 부분은 중요하면서 간단하다. 추상화에 의존해야지 구체화에 의존하면 안된다는 것이다. 로미오, 줄리엣을 예시로 로미오라는 역할을 줄리엣의 역할에 의존을 해야지 구현체인 김태희에 의존을 해서는 안된다.

위에서 얘기했지만 OCP 원칙에서 client는 역할에 대한 구현체를 알고 있어야 한다는 점이 있고 spring이 이를 해결해준다. 다형성만으로는 OCP, DIP를 지킬 수 없다는 점, 그 곳이 바로 spring의 출발점이다.

profile
풀스택개발자가되고싶습니다:)

0개의 댓글