NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

아이템 18. 상속보다는 컴포지션을 사용하라 #18

Closed madplay closed 5 years ago

madplay commented 5 years ago

상속(extends)

그럼 왜 최선이 아닐까?

메서드 호출과 다르게 상속은 캡슐화를 깨뜨린다.

// 객체 생성 후 3개의 엘리먼트를 addAll 메서드로 추가 MyHashSet mySet = new MyHashSet<>(); mySet.addAll(List.of("탱1","탱2","탱3"));

// 출력되는 값은? System.out.println(mySet.getAddCount());


- 위의 코드를 실행하면 addCount의 값이 3이 나올 것으로 기대했겠지만 실제로는 6이 반환된다.
- 원인은 바로 HashSet의 addAll 메서드가 add 메서드를 사용하여 구현되었기 때문이다.
- 그러니까, addAll 메서드는 각 요소를 add 메서드를 호출해서 추가하게 된다.
  - addAll에는 addCount를 증가시키는 코드가 없어야 한다.
- 이런 경우에는 하위 클래스에서 addAll 메서드를 재정의하지 않으면 문제를 고칠 수 있다.
```java
// HashSet(AbstractSet)의 addAll 메서드
public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}


메서드를 재정의하는 것보다 새로 만드는 게 조금 더 낫다.


그럼 어떻게 해야 안전할까?

public class ForwardingSet implements Set { private final Set set; public ForwardingSet(Set set) { this.set = set; } public void clear() { set.clear(); } public boolean isEmpty() { return set.isEmpbty(); } public boolean add(E e) { return set.add(e); } public boolean addAll(Collection<? extends E> c) { return set.addAll(c); } // ... 생략 }


- 다른 Set 인스턴스를 감싸고 있다는 뜻에서 MySet과 같은 클래스를 래퍼 클래스라고 한다.
- 또한 다른 Set에 계측 기능을 덧씌운다는 뜻에서 데코레이터 패턴(Decorator Pattern) 이라고 한다.
- 컴포지션과 전달의 조합은 넓은 의미로 위임(delegation)이라고 한다.
  - 엄밀히 따져서 래퍼 객체가 내부 객체에 자기 자신의 참조를 넘기는 경우만 해당

<br/>

## 그럼 언제 상속을 해야할까?
### 클래스 B가 클래스 A와 is-a 관계일때만 사용해야 한다.
- 반드시 하위 클래스가 상위 클래스의 진짜 하위 타입인 상황에서만 쓰여야 한다.
- 클래스 A를 상속하는 클래스 B를 만드려고 한다면, "B가 정말 A인가?"
  - 아니라면 A를 클래스 B의 private 인스턴스로 두자.
  - 즉, A는 B의 필수 구성요소가 아니라 구현하는 방법 중 하나일 뿐이다.
- 예를 들어보면? 와인 클래스를 상속하는 레드 와인 클래스.
  - 레드 와인은 와인이다.