다형성(polymorphism)이란?

2022. 3. 19. 21:02

다형성(polymorphism) = 객체지향의 꽃

역할(interface)과 구현(implementation)

  • 역할(interface)과 구현(implementation)으로 구분
    • 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

BELATED ARTICLES

more