IT/Programming / / 2023. 4. 27. 09:44

<Effective Java> RULE 16 계승하는 대신 구성하라

반응형

 

계승을 고려해 설계되고 그에 맞는 문서를 갖춘 클래스에 사용해야 안전하다(규칙 17)

메서드 호출과 달리, 계승은 캡슐화 원칙을 위반한다.

 

상위클래스의 구현은 릴리즈가 거듭될 때마다 달라질 수 있는데 그에따라 하위 클래스의 기능 또한 발맞춰 변화해야한다.

ex) HashSet에 객체를 추가하여 개수를 반환하는 코드

//잘못된 사례
public class InstrumnentedHashSet<E> extends HashSet<E> {
    private int addCount = 0;
    ~
    @Override public boolean add(E e){
        addCount++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c){
        addCount++;
        return super.addAll(c);
    }
    public int getAddCount() {
        return addCount;
    }
}

InstrumnentedHashSet<String> s = new InstrumnentedHashSet<String>();
s.addAll(Arrays.asList("Snap","Crackel","Pop"));

getAddCount하면 실제로는 6을 반환
#
HashSet의 AddAll은 add메서드를 통해 구현되어 있기 때문에 3을 더하고 
상위클래스인 HashSet의 addAll 메서드를 super.addAll과 같이 호출하는데
 이 메서드는 재정의한 add 메서드를 삽입할 원소마다 호출하게 됨.
 

구성 기법 : 기존 클래스를 계승하는 대신 새로운 클래스에 기존 클래스 객체를 참조하는 private필드를 하나 두는것

public class ForwardingSet<E> implements Set<E>{
    private final Set<E> s ;
 
    public ForwardingSet(Set<E> s) { this.s = s ; }
 
    public void add(E e) { return s.add(e); }
    ...
}
 

→ 장식자 패턴, Wrapper Class

→ 기존 클래스가 새로운 클래스의 일부가 된다.

 새로운 클래스에 포함된 각각메서드는 기존 클래스에 있는 메서드 가운데 필요한것만 찾아서 호출할 수 있고 그 결과를 반환 할 수 있다.

 기존 클래스 구현 세부사항에 종속되지 않아 견고하다.

 다른 메서드가 추가되더라도 영향이 없다.

계승은 강력한 도구지만 캡슐화 원칙을 침해하므로 문제를 발생시킬 소지가 있다는 것이다. 이런 문제를 피하려면 구성과 전달 기법을 사용하는 것이 좋다. 포장 클래스 구현에 적당한 인터페이스가 있다면 더욱 그렇다. 포장 클래스의 하위 클래스보다 견고하고 강력해진다.

 is-a관계가 성립할 때만 계승해야 한다, 그게 아니라면 객체 안에 private필드를 두고 더 작고 간단한 api를 구현해야 한다.

 구성기법이 적절한 곳에 계승을 할 경우 쓸데없는 정보가 노출되고 만다. 클래스의 성능을 개선하기 어렵고 클라이언트가 내부 구현 세부사항에 접근할 수 있게 된다.

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유