-
TL;DR
예시를 통해 문제상황 알아보기시나리오
시나리오를 반영한 InstrumentedHashSet.java public class InstrumentedHashSet<E> extends HashSet<E> {
// 추가된 원소의 수
private int addCount = 0;
public InstrumentedHashSet() {
}
public InstrumentedHashSet(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> ihs = new InstrumentedHashSet<>();
ihs.addAll(List.of("틱", "탁탁", "펑"));
컴포지션을 활용하여 문제 해결하기InstrumentedSet.java
public class InstrumentedSet<E> extends HashSet<E> {
// 추가된 원소의 수
private int addCount = 0;
public InstrumentedSet(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;
}
} ForwardingSet.java public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
@Override public int size() { return s.size(); }
@Override public boolean isEmpty() { return s.isEmpty(); }
@Override public boolean contains(Object o) { return s.contains(o); }
@Override public Iterator<E> iterator() { return s.iterator(); }
@Override public Object[] toArray() { return s.toArray(); }
@Override public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean add(E e) { return s.add(e); }
@Override public boolean remove(Object o) { return s.remove(o); }
@Override public boolean containsAll(Collection<?> c) { return s.containsAll(c); }
@Override public boolean addAll(Collection<? extends E> c) { return s.addAll(c); }
@Override public boolean retainAll(Collection<?> c) { return s.retainAll(c); }
@Override public boolean removeAll(Collection<?> c) { return s.removeAll(c); }
@Override public void clear() { s.clear(); }
} |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
TL;DR는 어떤 의미가 숨어있는 것 인가요? |
Beta Was this translation helpful? Give feedback.
-
구성을 활용하는 방식이 디자인 패턴의 여러 패턴에서도 많이 쓰이기에 정말 중요하다고 알 고 있고 @corock 님께서 설명해주신 예시 뿐만 아니라 전략 패턴 (Strategy Pattern) 에서도 쓰여서 해당 예시를 참고하는 것도 좋은 것 같아 올립니다. 정말 유명한 예인데 추상 클래스 Duck과 추상메서드 quack()이 있을때 오리 종류마다 quack을 다르게 구현하고 싶은 상황이 있다고 가정합시다. 상속을 이용한다면 오리 종류마다 quack 메서드를 재정의 할 수 있으니 좋다고 판단하여 상속을 이용해 구현해보았습니다. public abstract class Duck {
abstract void quack();
} public class JoisFeDuck extends Duck {
@Override
void quack() {
System.out.println("꾸에에엑");
}
} public class CoRockDuck extends Duck {
@Override
void quack() {
System.out.println("코엑");
}
} 문제점
해결책구성을 활용한 전략 패턴 전략 패턴 : 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 해줌
캡슐화 : 달리지는 부분(코드)을 찾아서 나머지 코드에 영향을 주지 않도록 분리하는 것.캡슐화를 하면 바뀌지 않는 부분엥 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있기에 유연한 코드가 됨! 그래서 전략 패턴을 적용해보면 public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {
}
} public class JoisFeDuck extends Duck {
public JoisFeDuck() {
super.quackBehavior = new JoQuack();
super.flyBehavior = new FlyWithWings();
}
} public class CoRockDuck extends Duck {
public CoRockDuck() {
super.quackBehavior = new CoQuack();
super.flyBehavior = new FlyNoWay();
}
} public interface QuackBehavior {
void quack();
} public class JoQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("꾸에에엑");
}
} public class CoQuack implements QuackBehavior {
@Override
public void quack() {
System.out.println("코엑");
}
} public interface FlyBehavior {
void fly();
} public class FlyWithWings implements FlyBehavior {
@Override
public void fly() {
System.out.println("날 수 있어!!");
}
} public class FlyNoWay implements FlyBehavior {
@Override
public void fly() {
System.out.println("못 날라 댕김");
}
} 달라지는 부분인 quack, fly를 캡슐화 하여 각각의 알고리즘 군을 수정해서 사용할 수 있게됨 특히 Duck에서는 상속이 아닌 구성을 통해 QuackBehavior, FlyBehavior 구현체를 가지게 함으로써 시스템을 만들었기에 유연성을 크게 향상시킬 수 있게 되었습니다. 즉 구성을 이용하여 단순히 알고리즘군을 별도의 클래스 집합으로 캡슐화 하였고 구성 요소로 사용하는 객체에서 올바른 행동 인터페이스를 구현하기만 하면 실행 시에도 행동을 바꿀 수 있는 유연한 코드가 되었습니다.
|
Beta Was this translation helpful? Give feedback.
상속보다는 구성(컴포지션)을 활용
디자인 원칙 중 정말 유명한 원칙이라 집중해서 정리하신 글을 읽었습니다.특히 변형된 HashSet을 구현하는데 상속을 이용한 방법과 그로 인한 문제점을 구성으로 해결하는 예제가 이해하는데 매우 좋았던 것 같습니다.
상속보다는 구성을 활용
한다는 것이 "A is a B" 보다 "A has a B"가 나을 수 있기 때문입니다. 한국말로 풀어보면 "A는 B이다" 보다는 "A에는 B가 있다"가 더 나을 수 있기 떄문입니다.구성을 활용하는 방식이 디자인 패턴의 여러 패턴에서도 많이 쓰이기에 정말 중요하다고 알 고 있고 @corock 님께서 설명해주신 예시 뿐만 아니라 전략 패턴 (Strategy Pattern) 에서도 쓰여서 해당 예시를 참고하는 것도 좋은 것 같아 올립니다.
정말 유명한 예인데 추상 클래스 Duck과 추상메서드 quack()이 있을때 오리 종류마다 quack을 다르게 구현하고 싶은 상황이 있다고 가정합시다.
상속을 이용한다면 오리 종류마다 quack 메서드를 재정의 할 수 있으니 좋다고 판단하여 상속을 이용해 구현해보았습니다.