다형성(polymorphism)이란?
다형성(polymorphism) = 객체지향의 꽃
역할(interface)과 구현(implementation)
- 역할(interface)과 구현(implementation)으로 구분
- ex) 운전자(역할) - 자동차(역할) - 자동차(구현)
- 운전자(클라이언트)는 자동차 역할에 의존하지 구현에 의존하지 않는다.
- 핵심은 자동차가 많은 구현을 할 수 있다는 것이 아니라, 어떠한 자동차가 나와도 클라이언트에 영향을 주지않고 새로운 기능을 추가할 수 있다는 것이다.
- ex) 로미오(역할) - 줄리엣(역할) ... 로미오(원빈, 장동건, 정우성 등), 줄리엣(송혜교, 김다미, 박소담 등)
- 로미오와 줄리엣은 배우가 될 수도 있고, 어떤 다른 사람이 와도 상관없다. 즉, 대체 가능해야한다
- ex) 운전자(역할) - 자동차(역할) - 자동차(구현)
- 역할과 구현으로 세상을 구분했을 때의 장점
- 클라이언트는 대상의 역할(인터페이스)만 알면 된다.
- 클라이언트는 구현 대상의 내부 구조를 몰라도 된다.
- 클라이언트는 구현 대상의 내부 구조가 변경되어도 영향을 받지 않는다.
- 클라이언트는 구현 대상 자체를 변경해도 영향을 받지 않는다.
- 객체를 설계할 때 역할과 구현을 명확하게 분리해야한다.
- 객체 설계 시 역할을 먼저 부여하고, 그 다음 그 역할을 수행하는 구현을 만든다.
객체의 협력이라는 관계부터 생각
- 혼자있는 객체는 없다.
- 다형성을 공부하면서 이런 생각을 할 수가 있다. 부모(Interface)가 있고 그걸 구현한 구현체들이 있는데 클라이언트는 없네? 라고 생각할 수 있는데, 클라이언트는 요청하는 사람, 서버는 응답하는 사람이라고 생각할 수 있다.
- 클라이언트 : 요청, 서버: 응답
- 수 많은 클라이언트와 서버는 서로 협력 관계를 가진다.
오버라이딩
- 다형성으로 인터페이스를 구현한 객체를 실행 시점에서 유연하게 변경할 수 있다.
- ex) 추상 팩토리 패턴과 전략패턴을 사용한 경우
다형성의 본질
- 다형성으로 인터페이스를 구현한 객체를 실행 시점에서 유연하게 변경할 수 있다.
- 따라서 인터페이스를 잘 설계하는 것이 중요하다.
- 다형성의 본질을 이해하려면 협력이라는 객체 사이의 관계에서 시작해야한다.
- 클라이언트를 변경하지 않고 서버 기능을 유연하게 변경할 수 있다.
public class UserService {
// private UserRepository userRepository = new UserJoinRepository();
private UserRepository userRepository = new UserFindRepository();
}
위 코드에서 클라이언트는 UserService 가 되며, 서버는 UserRepository(역할) 가 된다. 구현은 UserJoinRepository 와 UserFindRepository 이다. 위 코드는 다형성을 제대로 활용하지 못한 사례이다. Repository 구현체를 바꾸기 위해서는 클라이언트의 코드를 수정해야한다.
위 코드에서 클라이언트는 UserService 가 되며, 서버는 UserRepository(역할) 가 된다. 구현은 UserJoinRepository 와 UserFindRepository 이다. 위 코드는 다형성을 제대로 활용하지 못한 사례이다. Repository 구현체를 바꾸기 위해서는 클라이언트의 코드를 수정해야한다.
- MailService (역할)
- sendMail() (추상메서드)
- MailExternalService(외부망전용)
- MailInternalService(내부망전용)
public class MailVO {
private String uniqueNo; // 사번 및 회원 ID
private String title; // 제목
private String content; // 내용
// 생략
}
@Controller
public class MailController {
@PostMapping("/sendMail")
public String sendMail(MailVO mailVO) {
// 실행 시점에 클라이언트를 변경하지 않고 서버의 기능을 유연하게 변경할 수 있다.
MailService mailService = MailServiceFactory.findMailService(mailVO);
mailService.sendMail();
}
}
여기서 클라이언트는 MailController 이며 역할은 MailService 구현은 MailExternalService 와 MailInternalService 이다. 추상 팩토리 패턴을 통해서 mailVO 안에 있는 uniqueId 를 통해서
MailServiceFactory.findMailService 내부에서는 findEmployee 메서드를 실행한다. (where uniqueId = #uniqueId#) 만약에 findEmployee 의 결과가 null 이면 외부망이고 null 이 아니면 내부망 이라고 판단해서 메일을 발송한다.
스프링과 다형성
- 스프링은 다형성을 극대화하여 이용할 수 있게 해준다.
- 스프링의 IoC, DI 는 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 해준다.
- 스프링을 사용하면 구현을 편리하게 변경할 수 있다.
즉, 스프링의 핵심은 좋은 객체지향 프로그래밍을 할 수 있게 도와주는 도구이며 객체지향의 꽃은 다형성이고, 따라서 스프링에서 가장 중요한 요소는 IoC, DI 이며, 스프링은 다형성을 편리하게 사용할 수 있도록 해주는 도구이다.
참고 : 인프런 김영한님 스프링 핵심 원리 강의 - https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8#
'Spring' 카테고리의 다른 글
AOP란? (0) | 2022.03.20 |
---|---|
PSA란? (0) | 2022.02.23 |
접근제어자 종류와 기능 (0) | 2022.02.09 |
오버로딩(Overloading) VS 오버라이딩(Overriding) (0) | 2022.02.09 |
Java 코드의 컴파일 과정 (0) | 2022.02.08 |