티스토리 뷰
|
상속은 코드 재사용을 돕는 강력한 도구지만, 항상 최선이라고 할 수 없다.
메서드 호출과 달리, 상속은 캡슐화 원칙을 위반한다.
=> 하위 클래스가 정상 동작하기 위해서는 상위 클래스의 구현에 의존할 수 밖에 없다.
예제 클래스로 HashSet 클래스의 성능을 높이기 위해서 얼마나 요소가 추가되는지 확인하는 클래스를 작성해 본다.
//계승을 잘못 사용한 사례
public class InstrumentedHashSet<E> extends HashSet<E> {
//요소를 삽입하려 한 횟수
private int addCount = 0;
public InstrumentedHashSet() {
}
pulbic InstrmentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
괜찮아 보이지만 제대로 동작하지 않는다.
InstrumentedHashSet<String> s = new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("snap", "Cracle", "Pop"));
3을 반환해야 할 것 같지만 6을 반환하다.
HashSet의 addAll 메서드는 add 메서드로 구현되어 있기 때문이다.
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
이런 문제로 인해서 override 한 addAll 메서드를 수정하더라도 이 fix는 상위 클래스 addAll에 의존해버린다. 만약 상위 클래스 addAll 메서드가 수정되어 더이상 add 메서드를 호출하지 않는다면 또 다시 하위 클래스 구현이 깨져버릴 것이다.
또 다른 하위 클래스 구현을 망치는 요인은 상위 클래스에 새로운 메서드가 추가될 경우이다.
위에 2가지 문제를 해결하는 방법은 새로운 클래스에 기존 클래스 객체를 참조하는 private 필드를 하나 두는 것이다. 이런 설계 기법을 구성(composition) 이라고 부르는데, 기존 클래스가 새 클래스의 일부가 되기 때문이다.
public class InstrumentSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentSet(Set<E> s) {
super(s);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) {
this.s = s;
}
public boolean add(E e) {
return s.add(e);
}
public boolean addAll(E e) {
return s.addAll(e);
}
...
}
ForwardingSet 이라는 클래스가 Set 의 기능들을 전달(Forwarding)해주기 때문에 안정적이고 유연성이 높다. 또한 InstrumentedSet에 새로운 기능을 추가하기에도 용이하다
....
'JAVA > Effective Java' 카테고리의 다른 글
규칙18 추상 클래스 대신 인터페이스를 사용하라 (0) | 2018.12.19 |
---|---|
규칙17 계승을 위한 설계와 문서를 갖추거나, 그럴 수 없다면 계승을 금지하라 (0) | 2018.12.19 |
규칙15 변경 가능성을 최소화하라 (0) | 2018.12.19 |
규칙14 public 클래스 안에는 public 필드룰 두지 말고 접근자 메서드를 사용하라 (0) | 2018.12.19 |
규칙13 클래스와 멤버의 접근 권한은 최소화 해라 (0) | 2018.12.19 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- HashMap
- aurora
- N+1
- spring cloud gateway
- notifyAll()
- getBoolean
- router
- ConcurrentHashMap
- AbstractMethodError
- RouteDefinition
- wait()
- mariadb-connector-j
- referencedColumnName
- notify()
- reactor
- Lazy
- mariada-connector
- msyql-connector-java
- ResultSet
- circurit breaker
- GlobalFilter
- RoutePredication
- Flux
- DyanomoDB
- custom config data convertion
- rate limit
- reative
- Seperate Chaining
- dynamodb
- MariaDB
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
글 보관함