본문 바로가기
Language/Java

Favor composition over inheritance

by 태하팍 2015. 6. 16.
반응형

여기에서 말하는 상속의 범위
  - extends!!  interface implements는 아니다.

상속은 코드 재사용에 대해서 파워풀하지만 항상 좋은것만은 아니다.
: 상속을 적절히 잘 사용하지 못하면 소프트웨어는 깨지기 쉽다.

잘 사용하려면?
  • 같은 프로그래머가 같은 패키지 안에서 사용하면 안전하다(당연한거 아닌가..-_-;;)
  • 여러사람이 사용하더라도 상속을 고려해 설계되고 그에 맞는 문서를 갖춘 클래스에 사용하는 것은 안전!

메서드 호출과 달리, 계승은 캡슐화 원칙을 위반한다.
 : 하위 클래스는 상위클래스의 구현에 의존하기 때문! 상위클래스가 변경되면 하위에 영향이 감.

예제를 통해 더욱 더 신나는 이펙티브 자바의 내용을 알아보도록 하자! 
예제는 HashSet의 예제를 보도록 한다.
우선 Class UML을 하나보도록 하자.(완벽하지는 않다. 대충보도록 하자! ㅋ)


우리가 코딩할 예제의 클래스 구조이다.




위의 예제에서의 답은?!!
이유는 왜 그런걸까?

InstrumentedHashSet
mainObj.addAll(Arrays.asList("terry","ace-t","victor", "hoiru", "jinny"));

@Override
public boolean addAll(Collection<? extends E> c){
addCount +=c.size();
return super.addAll(c);
}

HashSet
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}

AbstractCollection
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}

InstrumentedHashSet
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}


해결 방법_1) @Override한 addAll()을 주석 처리 한다.
//  @Override
// public boolean addAll(Collection<? extends E> c){
// addCount +=c.size();
// return super.addAll(c);
//  }

해결 방법_2) addAll()메소드가 인자에 담긴 각 원소마다 add를 호출하도록 변경.
@Override
public boolean addAll(Collection<? extends E> c){
//addCount +=c.size();
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;

}

위의 해결방법이 모든 문제를 해결하는 것은 아니다. 상속을 함으로써 상위클래스와 하위클래스의 응집도가 매우 높기때문이다.

그래서 사용하는 것이 Wrapper Class를 두고 그 Class는 다른 객체를 Wrapper하고 있다.
쉽게 말해서 우리에게 잘알려진 Decorator Pattern을 통해 해결할 수 있다.
단, 역호출(CallBack) 프레임워크와 함께 사용하기에는 적합하지 않다. 웅? Wrapper 객체는 포장 객체에 대해서 모르기 때문에 자기 자신에 대한 참조(this)를 전달할 것이다. 이 문제는 SELF 문제(self problem lieberman 86)로 알려져있다고 한다.

Decorator Pattern 참조 : http://acet.pe.kr/386



반응형