[섹션 1. 객체 지향 설계와 스프링] 좋은 객체 지향 프로그래밍이란?(+다형성이란?)/ 인프런 김영한 스프링 핵심 원리 기본편
이번 시간에는 좋은 객체 지향 프로그래밍이 무엇인지 알아보는 시간을 가졌다.
객체 지향의 특징으로는,
추상화, 캡슐화, 상속, 다형성과 같은 특징들을 가지고 있다.
객체 지향 프로그래밍이란,
프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각각의 객체는 메세지를 주고받고, 데이터를 처리할 수 있다.
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.
유연하고 변경이 용이하다는 뜻은,
레고 블록을 조립하듯이, 키보드 마우스를 갈아 끼우듯이,
컴퓨터 부품을 갈아 끼우듯이 컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법이다.
- 다형성이란?
다형성을 설명하기 위해, 역할이 있고, 그 역할을 직접 행하는 구현으로 구분한다고 하자.
그러면 역할이 인터페이스이고, 구현이 실제로 인터페이스를 구현한 객체일 것이다.
다형성을 실제 세상에 비유를 해보자.
운전자와 자동차가 있다고 했을 때, 운전자의 역할이 있고, 자동차의 역할이 있을 것이다.
자동차에서는 자동차의 역할이 있을 것이고, 그 역할의 구현이 있을 것이다.
자동차의 역할은 이동 수단이고,
이 자동차 역할의 구현은, 예를 들어, K3라는 자동차가 구현을 하고, 아반떼라는 자동차가 구현을 할 것이다.
시점을 바꿔서, 운전자가 K3를 운전하다가, 아반떼로 자동차를 바꿨다고 하자.
그래도 운전이 가능한가?
물론 가능하다. 왜냐하면 자동차의 역할에 대한 구현만 바뀌었을 뿐이기 때문이다.
그렇기 때문에, 자동차가 바뀌어도 운전자에게 영향을 주지 않는다.
가능한 이유가 무엇이냐면, 자동차 역할이라는 인터페이스를 따라서 자동차를 구현했기 때문이다.
운전자는 자동차의 역할, 즉, 자동차의 인터페이스에만 의존을 하고 있기 때문이다.
자동차라는 역할을 만들고, 구현을 분리한 이유는 누구를 위해서일까?
바로 운전자를 위해서이다.
이 운전자를, 클라이언트라고 했을 때, 클라이언트가 자동차의 내부 구조를 몰라도 된다.
또한 구현체가 바뀌더라도 자동차 역할만 정상적으로 하고 있으면,
내부적으로 바뀌어도 클라이언트에게 영향을 주지 않는다.
또한, K3에서 아반떼로 바뀌어도 운전자는 바꿀 필요 없이 그대로 운전이 가능하다.
이렇게 자동차는 계속 출시가 가능하고, 무한으로 확장이 가능하다.
이 개념을 클라이언트에 적용하자면, 클라이언트에 영향을 주지 않고 새로운 기능을 제공할 수 있다는 뜻이다.
(=무한으로 확장이 가능하다.)
이것이 가능한 이유는 역할과 구현으로 세상을 구분했기 때문이다.
다른 예시를 들어보자.
로미오와 줄리엣이라는 공연이 있다고 가정하자.
로미오 역할에는 남자1, 남자2라는 배우가 있고,
줄리엣 역할에는 여자1, 여자2라는 배우가 있다고 하자.
배우는 대체가 가능해야되고, 이를 변경 가능한 대체 가능성이 생긴다고 표현한다.
이렇게 유연하고 변경이 용이하다는 뜻을 설명할 수 있다.
로미오가 클라이언트이고, 줄리엣이 서버가 된다고 가정해보자.
줄리엣의 구현이 바뀐다고 해서, 로미오의 역할에 영향을 주지 않고, 다른 대상으로 대체가 가능하다.
이런 상황을 유연하고 변경이 용이하다고 할 수 있다.
이렇게 클라이언트는 대상의 역할(인터페이스)만 알면 되고, 구현 대상의 내부 구조를 몰라도 된다.
또한 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않고,
구현 대상 자체를 변경해도 영향을 받지 않는다.
이렇게 역할과 구현을 분리하는 개념을 자바 언어에서도 차용을 했고, 자바 언어에서 다형성을 활용할 수 있다.
역할을 인터페이스,
구현을 인터페이스를 구현한 클래스 혹은 구현 객체로 볼 수 있다.
- 객체 지향 설계의 순서
이렇게 객체를 설계할 때, 역할과 구현을 분리해서 설계를 하고,
객체 설계 시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체를 만드는 순서로 진행된다.
핵심을 보자면, 구현보다 인터페이스가 먼저라는 것이다!
설계를 할 때는, 혼자있는 객체는 없기 때문에, 객체의 협력이라는 관계부터 생각해야된다.
클라이언트는 요청하는 사람, 서버는 그 요청에 응답하는 사람이라고 보면 된다.
그래서 많은 객체 클라이언트와 객체 서버는 협력 관계를 가진다.
자바 언어의 다형성은 오버라이딩을 떠올려보자.
다형성은 오버라이딩을 통해 동작하고, 결과적으로 오버라이딩 된 메소드가 실행이 된다.
다형성의 장점으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경할 수 있다.
물론 클래스 상속 관계도 다형성, 오버라이딩 적용이 가능하다.
코드에서 보면 이렇게 MemberService라는 클라이언트에 MemberRepository라는 인터페이스에
MemoryMemberRepository나 JdbcMemberRepository라는 구현체를 바꿔서 낄 수 있다~~
DB에 저장을 하고 있다가, Memory에 저장을 하기 위해 변경을 하고 싶으면,
기존의 서비스 로직을 수정하거나, 다른 코드들에게 영향을 주지 않고,
MemoryMemberRepository를 구현해서 적용하면 Memory에 저장을 할 수 있게 된다~~
- 다형성의 본질
다형성의 본질은 인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경할 수 있다.
다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야 한다.
즉, 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
한계점도 있는데,
역할(인터페이스) 자체가 변하면, 클라이언트, 서버 모두에 큰 변경이 발생하게 된다.
예를 들어, 자동차가 비행기로 변경해야 된다면?
많은 기능이 추가되고, 구현체도 전부 수정해야 될 수도 있다.
그렇기 때문에, 인터페이스를 안정적으로 잘 설계하는 것이 중요하다.
앞서 언급한 것과 같이, 객체 지향의 꽃은 다형성이다.
스프링은 이 다형성을 극대화하여 이용할 수 있게 해준다.
스프링에서 제어의 역전(IoC), DI는 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원한다.
위에서 다룬 예제들과 같이, 자동차를 변경하듯이 구현을 편리하게 변경할 수 있다는 장점이 있다.