본문 바로가기
Programming/객체지향프로그래밍입문

# 다형성과 추상화

by JKROH 2023. 1. 19.
반응형

Main Point : 추상화는 유연한 변경이 가능하게 한다.


- 다형성

  • 다형성은 여러 모습을 갖는 것이다.
  • 객체 지향에서의 다형성은 한 객체가 여러 타입을 갖는 것이다. 즉, 한 객체가 여러 타입의 기능을 제공할 수 있다.
  • 다형성은 보통 타입 상속으로 구현한다. 어떤 타입을 상속받은 하위 타입은 상위 타입도 될 수 있다. 즉, 한 타입이 여러 타입을 갖게 된다.
public class Timer {
    public void start() {
        ...
    }
    
    public void stop() {
        ...
    }
}
    
public interface Rechargeable {
    void charge();
}

public class IotTimer extends Timer implements Rechargeable {
    public void charge() {
        ...
    }
}

public class Example {

    IotTimer it = new IotTimer();
    
    it.start();
    it.stop();
    
    Timer t = it;
    t.start();
    t.stop();
    
    Rechargeable r = it;
    r.charge();
}
더보기

 위의 예시에서 IotTimer 객체는 Timer 객체와 Rechargeable 인터페이스를 상속받고 있다. 즉, 두 타입이 모두 될 수 있다. 따라서 Example 클래스에서 IotTimer객체를 Timer에도 할당할 수 있고 Rechargeable에도 할당할 수 있는 것이다.

 

- 추상화

  • 추상화는 데이터나 프로세스 등을 의미가 비슷한 개념이나 의미 있는 표현으로 정의하는 과정이다.
  • 이러한 과정을 통해 기존의 객체들에서 새로운 개념을 뽑아내거나 표현을 도출한다.
  • 추상화에는 특정한 성질을 뽑아내는 방식과 공통 성질을 뽑아내는 두 가지 방식이 있다.
  • 특정한 성질을 뽑아내는 방식의 예
    • 사용자에게서 id, 이름, 전화번호 등을 뽑아내 DB의 User 테이블로 추상화할 수 있다.
    • 돈과 관련된 특징들에서 통화, 금액을 가지고 Money 클래스로 추상화 할 수 있다.
  • 공통 성질을 뽑아내는 방식의 예
    • 갤럭시, 아이폰, 노키아 등은 공통된 성질인 스마트폰으로 추상화가 가능하다.
    • 지포스, 라데온은 공통된 성질인 GPU로 추상화가 가능하다.
  • 다형성은 공통 성질을 뽑아내는 추상화 방식과 관련되어 있다.

 

- 서로 다른 구현 추상화

  • 다음의 세 가지 기능을 구현했다고 해보자.
    • 풍경화를 그린다.
    • 인물화를 그린다.
    • 정물화를 그린다.
  • 위의 세 기능의 공통점은 그림을 그린다라는 점이다.
  • 따라서 이 셋을 추상화해 그림 그리기로 표현할 수 있다.

 

- 타입 추상화

  • 타입 추상화는 구현을 추상화 할 때 사용한다.
  • 여러 구현 클래스가 공통점이 있을 때 이를 추상화 한 상위 타입의 도출이 가능하다.
  • 다음의 세 클래스가 존재한다고 가정해보자.
    • EmailNotifier
    • SMSNotifier
    • KakaoNotifier
  • 세 클래스는 공통적으로 Notifier로 추상화가 가능하다.
  • 이 때 추상화한 타입은 주로 인터페이스로 표현한다. 추상화한 타입과 구현 클래스는 타입 상속으로 연결한다.
  • 추상화한 타입은 서로 다른 클래스들의 공통된 특징을 표현한다. 이러한 표현은 곧 추상화한 타입이 어떤 기능을 제공하는지를 의미한다. 추상화한 타입은 어떤 방식으로 구현될지 모르기 때문에 구체적은 구현은 제공하지 않는다.
  • 위의 예시에서 Notifier는 '알림을 보낸다는 기능을 제공한다'는 점은 알고 있다. 그러나 그것이 이메일을 통한 알림인지, 문자를 통한 알림인지, 카카오톡을 통한 알림인지는 알 수 없다.
  • 따라서 구체적인 구현은 추상화한 타입을 상속받은 클래스들(EmaiNotifier, SMSNotifier, KakaoNotifier)에서 맡는다.
  • 이 때 구현을 제공하는 클래스들을 흔히 콘크리트 클래스라고 표현한다.

 

- 추상 타입 사용

  • 추상 타입은 기능의 구현은 감추지만 기능의 의도를 더 잘 드러낸다.
Notifier notifier = getNotifier();
notifier.notify(someNoti);
더보기

 위의 Notifier는 getNotifier가 어떤 타입의 객체를 리턴하는지에 상관없이 무언가를 notify한다는 의도를 드러낼 수 있다.

 

- 추상 타입 사용에 따른 이점 : 유연함

  • 콘크리트 클래스를 직접 사용하게 되면 요구 사항의 변경을 반영하기 위해 기능의 본질과는 관계없는 코드의 변경이 일어나게 된다.
  • 아래의 예시를 통해 살펴보자.
private SmsSender smsSender;

public void cancel() {
    
    주문 취소 처리 기능 구현
    
    smsSender.sendSms();
}
더보기

 최초에는 주문을 취소하면 문자를 보내는 요구 사항이 있었다. 따라서 cancel 메소드 내에 주문 취소 처리 기능을 구현하고 마지막에 smsSender를 이용하여 문자를 보내도록 구현했다.

 그런데 카카오 푸시가 가능하면 카카오톡으로 알림을 보내라는 요구가 추가되었다. 따라서 kakaoPush 클래스를 만들고 cancel 기능에 카카오톡으로 알림을 보내는 기능을 추가했다.

private SmsSender smsSender;
private KakaoPush kakaoPush;

public void cancel() {
    
    주문 취소 처리 기능 구현
    
    if (pushEnabled) {
        kakaoPush.push();
    }else{
        smsSender.sendSms();
    }
}
더보기

 그런데 이번에는 이메일로도 취소 사항을 알리라는 요구 사항이 추가되었다. 그래서 mailService라는 클래스를 만들고 메일을 보내는 코드도 추가했다.

private SmsSender smsSender;
private KakaoPush kakaoPush;
private MailService mailService;

public void cancel() {
    
    주문 취소 처리 기능 구현
    
    if (pushEnabled) {
        kakaoPush.push();
    }else{
        smsSender.sendSms();
    }
    mailService.sendMail();
}
더보기

 위의 코드 변경 과정에서 요구 사항이 계속해서 추가되었어도 주문 취소 처리라는 cancel 메소드의 핵심 기능은 전혀 변하지 않았다. 즉, 기능의 본질과는 상관없는 다른 요구 사항 때문에 본질을 구현하는 기능에 변화가 생겨버린 것이다. 

 이를 없애기 위해 문자, 카카오톡, 이메일의 공통점을 추상화해서 알림 즉, notifier라는 인터페이스를 도출한다. 이후 notifier를 통해 알림 기능을 수행하도록 코드를 수정하면 getNotifier가 상황에 따라서 알맞게 알림을 리턴하도록 할 수 있다.

public void cancel() {
    
    주문 취소 처리 기능 구현
    
    Notifier notifier = getNotifier();
    notifier.notify();
}

private Notifier getNotifier() {
    if (pushEnabled) {
        return new KakaoNotifier();
    }
    return new SmsNotifier();
}
더보기

 이 때 notifier를 보내는 것도 NotifierFactory로 추상화 할 수 있다.

 

- 추상화 결과 : 사용 대상의 변경이 유연해짐

  • 추상화를 하면 내가 사용하는 대상을 쉽게 변경할 수 있는 유연함을 얻을 수 있다.
  • OrderCancelService 클래스는 주문 취소(cancel 메소드)를 제공하는 클래스이다. 이 때 해당 클래스는 Notifier와 NotifierFactory 인터페이스를 사용한다.
  • 따라서 취소 결과에 대한 알림을 어떻게 보낼 것인지를 결정하는 과정과 방법이 바뀐다고 하더라도 OrderCancelService클래스에는 변화가 없다.

 

- 추상화는 의존 대상이 변경하는 시점에

  • 추상화를 한다는 것은 곧 새로운 추상 타입이 생긴다는 것을 의미한다.
  • 타입이 늘어나게 되면 그만큼 프로그램의 복잡도 역시 증가한다.
  • 따라서 아직 존재하지 않는 기능에 대해 너무 빨리 추상화를 하면 안된다. 이는 잘못된 추상화의 가능성을 높이고 프로그램의 복잡도를 불필요하게 높일 수 있다.
  • 결과적으로 추상화는 실제로 변경이나 확장이 발생할 때 시도해야 한다.

 

- 추상화를 잘 하려면

  • 추상화를 잘 하기 위해서는 구현을 한 이유가 무엇 때문인지를 생각해야 한다.
  • 추상화는 기본적으로 개념을 도출하는 것이다. 개념을 도출하기 위해서는 구현의 의도를 정확하게 알고 있어야 한다. (그렇기 때문에 '왜'라는 질문을 끊임없이 던져야 한다.)
  • SMSNotifier, KakaoNotifier, EmailNotifier를 구현할 때, 이들이 어떤 공통점을 지니고 있는지를 생각해볼 수 있다. 다시 말해, 이들을 '왜' 구현했는지를 생각하다보면 이들이 어떤 공통점을 지니고 있는지 알 수 있다.
  • 이 때 누군가는 이들이 무언가를 '알리기 위한' 수단이라고 추상화 할 수도 있고, 누군가는 이들이 무언가를 '전달하기 위한' 수단이라고 추상화 할 수도 있다.
  • 추상화의 결과가 무엇이던간에, 실제로 그 구현을 한 이유가 무엇 때문인지를 생각해야만이 좋은 추상화 결과를 얻을 수 있다.
반응형

'Programming > 객체지향프로그래밍입문' 카테고리의 다른 글

# 기능과 책임 분리  (0) 2023.01.28
# 상속보다는 조립  (0) 2023.01.27
# 캡슐화  (0) 2023.01.16
# 객체  (0) 2023.01.16
# 들어가며  (0) 2023.01.15

댓글