다형성(Polymorphism)
다형성은 객체지향의 특징 중 하나이다!
쉽게 말해 하나의 객체가 여러가지 타입을 가질 수 있는 것을 의미해.
딱 위의 개념을 자바 문법에 대입해 보면 한가지 떠오르는 놈이 있지! 바로~ 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조하는 것!! 또는 인터페이스로 그 구현체를 참조하는 것!!
실세계와 객체 지향이 완전히 1:1로 매칭이 되는 것은 아니나 역할과 구현을 분리해서 생각하면 실세계를 통해 다형성을 이해하기 수월해!!
한가지 예를 들어볼게!!
자동차라는 역할이 있고 이를 구현하는 것이 k3, 아반떼, 소나타라고 생각해보자. 운전자의 입장에서는 자동차라는 역할에 대해서만 안다면 이를 구현하는 것이 k3이든 아반떼이든 소나타이든 운전을 할 수 있따!!
엑셀을 밟으면 앞으로!! 브레이크를 밟으면 멈추고!! 이런 자동차의 역할을 안다면 이를 구현하는 놈의 종류는 상관이 없이 운전이 가능하단거쥐!!
하나 더 예를들어 본다면, 역할극에서 A라는 역할과 B라는 역할이 존재한다고 생각해보자. A 역할을 장동건이 하게 되었다고 가정하였을 때 장동건 입장에서는 B의 배우로 아이유가 오던 송혜교가 오던 대본에만 충실하면 전혀 문제가 될 것이 없다!! 이는 B역할을 하는 배우에게도 마찬가지인 이야기!
지금까지 이야기한 것이 바로 다형성이야!!
역할과 구현으로 구분하면 단순해지고, 유연해지며 변경도 편리해져!!
즉, 클라이언트는 대상의 역할만 알면되고 그 역할의 구현대상의 내부구조는 전혀 몰라도 돼!!
그리고 역할의 구현대상이 변경되어도 클라이언트는 영향을 받지 않아. 더 나아가 구현 대상 자체를 변경해도 영향을 받지 않지!!
이를 자바에 대입하면
역할 → 인터페이스
구현 → 인터페이스를 구현한 클래스, 구현 객체
라고 생각할 수 있어.
결과적으로 자바의 인터페이스를 잘 활용한다면 클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경하는 구조로 프로그래밍이 가능해지는 거야!!
이를 잘 기억해두고 그다음 개념으로 좋은 객체지향 설계의 5가지 원칙을 한번 보자!
SOLID
Single Responsibility Principle
이는 단일 책임 원칙이라고도 하며 그 의미는 다음과 같다.
한 클래스는 하나의 책임만 가져야한다! 이 책임이라는 것의 기준은 해당 부분에 변경이 있을 때 전파되는 영향이 적다면 위 원칙을 잘 준수했다고 생각하면 될 거 같다!
Open Closed Principle
이는 개방-폐쇄 원칙이라고도 하며 그 의미는 다음과 같다.
소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야한다!
Liskov Substitution Principle
이는 리스코프 치환 법칙이라고도 하며 그 의미는 다음과 같다.
프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다!
이는 컴파일 성공이 아니라 그 정확성에 관한거야. 예를들어 자동차의 엑셀은 앞으로가는 기능인데 자동차 역할을 구현한 놈이 엑셀을 뒤로가게 구현해버리는 걸 비유할 수 있어. 이를 코드로 생각한다면 컴파일에서는 문제가 없으나 정확성 부분에서 심각하게 위반한다는 것을 알 수 있지.
Interface Segregation Principle
이는 인터페이스 분리 원칙이라고도 하며 그 의미는 다음과 같다.
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 좋다는 거다!
예를들어, 커다란 자동차 인터페이스 보단 이를 운전 인터페이스와 정비 인터페이스로 분리하는 것이 좋고
사용자도 운전자와 정비사로 분리하면 좋다!!
왜냐!?!? 이렇게 할 경우 정비 쪽 수정 부분이 생겨도 운전 부분에는 영향을 가하지 않게 끔 구조를 짤 수 있기 때문이지!!
Dependency Inversion Principle
이는 의존관계 역전 원칙이라고도 하며 그 의미는 다음과 같다.
프로그래머는 추상화에 의존해야하며 구체화에 의존해서는 안된다!
자~ 그럼 이까지 공부를 해보았는데 이제 이야기해보고 싶은것은 바로!! 다형성만으로 위의 OCP와 DIP를 지킬 수 있느냐다!!
OCP가 뭐라고 했던가? 변경은 X 확장은 O!!
DIP는?? 추상화 의존!!
자 그럼 한가지 가정을 해본다. 어떤 것(Something) DB에 접근하는 Repository가 있고 이를 SomethingRepository라고 해보자.
얘는 지금 아직 디비를 정하지 않아 다형성 개념을 살려서 저녀석을 인터페이스로 두고 우선적으로 메모리 기반으로 구현체를 만들었다(SomethingMemRepository)!! 이후에 이제 디비가 정해져서 (SomethingDBRepository)로 수정하려 한다!!
코드 상에서 본다면
1 | SomethingRepository sr = new SomethingMemRepository(); |
위의 상황을 보면 알 수 있듯이 new SomethingMemRepository() 를 지우고 new SomethingDBRepository()로 바꾸어주어야 한다!!
기능을 확장함에 있어 변경이 일어나 버렸따… (OCP 위배…)
결과적으로 구현하는 구현체에 의존하는 현상이 일어나 버렸따…(DIP 위배..)
그럼 어떻게 해야할까?? 🤔🤔🤔
이는 Spring을 활용하면 말끔히 해결할 수 있따!! Spring의 핵심개념인 IOC 혹은 DI라고도 하는 개념을 이용하면 조금 더 객체지향스럽게!! SOLID 원칙을 준수하며 개발을 할 수 있게 된다!!
Spring은 객체지향을 조금 더 객체지향스럽게 도와주는 도구라고 생각할 수 있다!!
(이와 관련해서는 따로 다루도록 하즈아!!)