
package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
// 코드 18-1 잘못된 예 - 상속을 잘못 사용했다! (114쪽)
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;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(List.of("틱", "탁탁", "펑"));
System.out.println(s.getAddCount());
}
}

상속의 내부 구현을 알아야 한다.
package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
// 코드 18-1 잘못된 예 - 상속을 잘못 사용했다! (114쪽)
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;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(List.of("틱", "탁탁", "펑"));
System.out.println(s.getAddCount());
}
}
하위 클래스에서 기능을 추가한 경우 counting을 해서 개수를 새겠다.
private int getAddCount() {
return addCount;
}
private으로 정의한다 했는데 상위 클래스에서 나중에 getAddCount() int 타입으로 정의한다면 그러면 또 깨진다.
이거에 대한 대안으로 컴포지션을 정의한다.
컴포지션은 기존 클래스를 확장하는 것이 아니라 우리가 재사용하고 싶은 기능들을 모두 가지고 있는 이 클래스를 멤버 변수로 선언을 하는 것이다.
package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
// 코드 18-3 재사용할 수 있는 전달 클래스 (118쪽)
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection<?> c)
{ return s.containsAll(c); }
public boolean addAll(Collection<? extends E> c)
{ return s.addAll(c); }
public boolean removeAll(Collection<?> c)
{ return s.removeAll(c); }
public boolean retainAll(Collection<?> c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o)
{ return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
set을 받아온다음에 모든 메소드는 받아온 것을 통해서 사용한다.
package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
// 코드 18-2 래퍼 클래스 - 상속 대신 컴포지션을 사용했다. (117-118쪽)
public class InstrumentedSet<E> extends ForwardingSet<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;
}
public static void main(String[] args) {
InstrumentedSet<String> s = new InstrumentedSet<>(new HashSet<>());
s.addAll(List.of("틱", "탁탁", "펑"));
System.out.println(s.getAddCount());
}
}

package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
// 코드 18-1 잘못된 예 - 상속을 잘못 사용했다! (114쪽)
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);
}
private int getAddCount() {
return addCount;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
s.addAll(List.of("틱", "탁탁", "펑"));
System.out.println(s.getAddCount());
}
}
기존의 코드에서 6이 나온 이유는 addAll에서 상위 함수로 가면 해당 함수에 add를 사용할 때 상속한 add를 호출하게 되기 때문이다.
컴포지션의 구조에서는 addAll을 호출했을 때 위임하는 애를 가지고 오기 때문에 자기가 가지고 있는 애를 사용 안하기 때문에 오류가 없다.


package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
// 코드 18-3 재사용할 수 있는 전달 클래스 (118쪽)
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) { this.s = s; }
public void clear() { s.clear(); }
public boolean contains(Object o) { return s.contains(o); }
public boolean isEmpty() { return s.isEmpty(); }
public int size() { return s.size(); }
public Iterator<E> iterator() { return s.iterator(); }
public boolean add(E e) { return s.add(e); }
public boolean remove(Object o) { return s.remove(o); }
public boolean containsAll(Collection<?> c)
{ return s.containsAll(c); }
public boolean addAll(Collection<? extends E> c)
{ return s.addAll(c); }
public boolean removeAll(Collection<?> c)
{ return s.removeAll(c); }
public boolean retainAll(Collection<?> c)
{ return s.retainAll(c); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
@Override public boolean equals(Object o)
{ return s.equals(o); }
@Override public int hashCode() { return s.hashCode(); }
@Override public String toString() { return s.toString(); }
}
여기서 Set Interface가 컴포넌트이다.
package me.whiteship.chapter04.item18;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
// 코드 18-2 래퍼 클래스 - 상속 대신 컴포지션을 사용했다. (117-118쪽)
public class InstrumentedSet<E> extends ForwardingSet<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;
}
public static void main(String[] args) {
InstrumentedSet<String> s = new InstrumentedSet<>(new HashSet<>());
s.addAll(List.of("틱", "탁탁", "펑"));
System.out.println(s.getAddCount());
}
}
HashSet이 Concrete 컴포넌트에 해당한다.
Decorator에 해당하는게 Forwarding Set에 해당한다.
Concrete Decorator에 해당하는게 InstrumentedSet이 된다.

package me.whiteship.chapter04.item18.callback;
interface FunctionToCall {
void call();
void run();
}
package me.whiteship.chapter04.item18.callback;
class BobFunction implements FunctionToCall {
private final Service service;
public BobFunction(Service service) {
this.service = service;
}
@Override
public void call() {
System.out.println("밥을 먹을까..");
}
@Override
public void run() {
this.service.run(this);
}
}
package me.whiteship.chapter04.item18.callback;
public class Service {
public void run(FunctionToCall functionToCall) {
System.out.println("뭐 좀 하다가...");
functionToCall.call();
}
public static void main(String[] args) {
Service service = new Service();
BobFunction bobFunction = new BobFunction(service);
bobFunction.run();
// BobFunctionWrapper bobFunctionWrapper = new BobFunctionWrapper(bobFunction);
// bobFunctionWrapper.run();
}
}

package me.whiteship.chapter04.item18.callback;
public class BobFunctionWrapper implements FunctionToCall {
private final BobFunction bobFunction;
public BobFunctionWrapper(BobFunction bobFunction) {
this.bobFunction = bobFunction;
}
@Override
public void call() {
this.bobFunction.call();
System.out.println("커피도 마실까...");
}
@Override
public void run() {
this.bobFunction.run();
}
}
package me.whiteship.chapter04.item18.callback;
public class Service {
public void run(FunctionToCall functionToCall) {
System.out.println("뭐 좀 하다가...");
functionToCall.call();
}
public static void main(String[] args) {
Service service = new Service();
BobFunction bobFunction = new BobFunction(service);
BobFunctionWrapper bobFunctionWrapper = new BobFunctionWrapper(bobFunction);
bobFunctionWrapper.run();
}
}

this.service.run(this);
이 부분에서 this가 self이다. BobFunction을 의미하게 된다.
bobFunction의 wrapper를 안 부르고 bobFunction의 call을 사용한다.
package me.whiteship.chapter04.item18.callback;
public class Service {
public void run(FunctionToCall functionToCall) {
System.out.println("뭐 좀 하다가...");
functionToCall.call();
}
public static void main(String[] args) {
Service service = new Service();
BobFunction bobFunction = new BobFunction(service);
BobFunctionWrapper bobFunctionWrapper = new BobFunctionWrapper(bobFunction);
bobFunctionWrapper.call();
}
}
제대로 쓰려면 이와 같이 써야 한다.
