객체 지향 프로그래밍 입문 - 상속보단 조립

Lee Han Sol·2021년 9월 18일
0
post-thumbnail
post-custom-banner

객체 지향 프로그래밍 입문

이 글은 최범균님의 Inflearn 강의를 학습한 내용을 정리하였습니다.

상속

상속 정의
상위 클래스를 확장한다는 말이다.
상속하면 상위 클래스의 public 또는 protected로 선언된 모든 변수와 메소드를 사용할 수 있다.

상속 활용 방안
상위 클래스의 기능을 재사용, 확장하는 방법으로 활용된다.

상속을 설명할 예로 스프링 프레임워크의 웹 요청을 처리하기 위한 초기 클래스 계층 구조를 가져왔다.

위쪽의 빨간색 테두리쳐진 AbstractController웹 요청과 관련된 기본 기능을 제공한다.

그 아래의 주활색 테두리쳐진 BaseCommandController웹 요청 기본 기능 + 파라미터 처리 기능을 확장하고 있다.

마지막으로 AbstractFormController상위 클래스 기능 + Form관련 기능(폼 보여주기, 폼 전송 처리)을 확장한다.

이렇듯 상속은 상위 클래스의 기능을 재사용하고 확장하는 방법으로 활용한다.
하지만, 상속을 통한 기능 재사용은 몇가지 문제점이 생긴다.

  1. 상위 클래스 변경 어려움
  2. 클래스 증가
  3. 상속 오용

상속 활용의 단점

1. 상위 클래스 변경 어려움

첫 번째로 알아볼 내용은 상위 클래스 변경 어려움이다.

상위 클래스를 변경하게 되면 그 변경이 모든 하위 클래스에 영향을 줄 수 있다.
또한, 상위 클래스는 하위 클래스에 대한 캡슐화가 약해질 수 있다.
하위 클래스는 기능 재사용을 위해 상위 클래스의 동작을 알고 있어야하기 때문이다.

2. 클래스 증가

두 번째는 클래스 증가이다.

왼쪽 그림 설명

처음에는 Storage를 Compressed(압축)하고 Encrypted(암호화)한 기능을 확장한 클래스만 있으면 충분했다.

오른쪽 그림 설명

시간이 흐르고 기존 2가지 기능에 Cacheable(캐시) 기능이 추가되고
독립적으로 사용하던 기능을 합쳐서 사용할 경우가 생겼다.
그래서 CompressedEcrypted(압축 + 암호화), EncryptedCompressed(암호화 + 압축) 같은 클래스를 만들었다.(오른쪽 그림)

이와 같은 방식으로는 새로운 기능(e.g.압축 + 암호화 + 캐시)을 지원해야된다면 또 다른 클래스가 생길 것이다.

압축 + 암호화 + 캐시 기능 클래스 추가

3. 상속 오용

마지막으로 상속을 사용한 확장 방식에 생길수 있는 단점은 상속 자체를 오용할 수 있다는 것이다.

이번 단점의 설명을 위해 아래의 ArrayList를 상속받은 Container 클래스를 보자.

//Container.java
public class Container extends ArrayList<Luggage> {
  private int maxSize;
  private int currentSize;
  
  public Container(int maxSize) {
    this.maxSize = maxSize;
  }
  
  
  public void put(Luggage lug) throws NotEnoughSpaceException {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    super.add(lug);
    currentSize += lug.size();
  }
  
  public void extract(Luggage lug) {
    super.remove(lug);
    this.currentSize -= lug.size();
  }
  
  public boolean canContain(Luggage lug) {
    return maxSize >= currentSize + lug.size();
  }
}

Container.java 코드 설명

Container 클래스는 Luggage 목록을 관리하는 클래스이다.

목록 관리 기능은 직접 구현하지 않고 ArrayList를 상속받아서 구현하였다.

관리 구현은 어떻게 했는지 보자.

  1. Luggage 추가
    public void put(Luggage lug) throws NotEnoughSpaceException {...}를 보면 super.add(lug);를 통해 수화물을 추가한다.

  2. Luggage 제거
    public void extract(Luggage lug) {...}을 보면 super.remove(lug)를 통해 수화물을 제거한다.

코드 자체로는 문제가 없다.

그리고 실제 사용은 아래와 같다.

그럼 문제는 어디서 발생해서 단점이 되는 걸까?

아래의 그림을 보자.

여기서 container에 luggage를 더하는 방법은 add()가 아닌 put()이다.

Container 클래스가 ArrayList의 add()를 지원하는 이유?
>> ArrayList를 상속받았기 때문이다.

add()를 통해 luggage를 추가하게 되면 오른쪽 코드의 주석에 나와있듯 container의 적재 가용량이 줄지 않아서 무한정 luggage를 싣게되는 상황이 발생한다.

여기까지 상속 방식을 통한 재사용 + 확장의 단점 3가지를 알아보았다.

그럼 상속말고 재사용 + 확장하기 위한 다른 방법을 알아보자.

조립

조립이란
기능을 재사용하고 싶은 클래스가 있다면 기능을 제공하는 클래스를 필요한 시점에 생성해서 사용하는 방식을 말한다.

조립 방식
필드로 다른 객체를 참조하는 방식으로 조립한다.
또는 객체가 필요한 시점에 생성/호출한다.

조립 방식 이점
여러 객체를 묶어서 더 복잡한 기능을 제공할 수 있다.

아래의 예를 보자.

FlowController에 암호화 기능 사용하고자 한다.
상속 방식의 경우 FlowController와 암호화 기능을 지원하는 객체를 상속받아서 구현했을 것이다.
하지만 조립 방식의 경우는 아래와 같이 사용한다.

public class FlowController {
  private Encryptor encryptor = new Encryptor(); //필드로 조립
  
  public void process() {
    ...
    byte[] encryptedData = encryptor.ecrypt(data):
    ...
  }
}

조립 방식 이점

클래스 증가 해결

조립 방식으로 기능을 재사용하면 앞서 보았던 클래스가 늘어나던 문제를 해결할 수 있다.

압축, 암호화, 캐시 기능을 사용하고 싶다면
왼쪽의 상속을 통한 기능 재사용이 아닌
오른쪽 그림처럼 해당 기능을 제공하는 객체를 조립하여 사용하면 된다.

상속 오용 해결

조립 방식을 사용하면 상속 오용의 가능성을 줄여준다.

아래의 코드를 보자.

public class Container extends ArrayList<Luggage> {
  private int maxSize;
  private int currentSize;
  
  public void put(Luggage lug) {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    super.add(lug);
    currentSize += lug.size();
  }
  ...
}

상속 방식은 외부에서도 상위 클래스의 기능도 모두 사용할 수 있도록 해준다.
그래서 3. 상속 오용 부분에 작성한 문제가 발생할 수 있다고 말하였다.

조립 방식은 해당 객체가 외부로 제공할 기능을 선택할 수 있다.
(불필요한 기능은 제공하지 않는다.)

public class Container {
  private int maxSize;
  private int currentSize;
  private List<Luggage> luggages = new ArrayList<>();
  
  public void put(Luggage lug) {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    luggages.add(lug);
  }
  ...
}

이처럼 기능 재사용을 할 때는 상속보단 조립 방식이 이점을 더 많이 가져다줄 수 있다.

상속보단 조립을 권장하는 이유

1. 불필요한 클래스가 생기지 않음

2. 상속 오용을 방지

상속 vs 조립 방식 선택

우선 기능을 재사용하기 위한 방식으로는 조립으로 풀 수 없는지 검토를 한다.
상속은 진짜 하위 타입인 경우만 사용하도록 한다.

profile
주 7일, 배움엔 끝이 없다
post-custom-banner

0개의 댓글