๐ Chapter 11: ํฉ์ฑ๊ณผ ์ ์ฐํ ์ค๊ณ
- ํฉ์ฑ์ ์ ์ฒด๋ฅผ ํํํ๋ ๊ฐ์ฒด๊ฐ ๋ถ๋ถ์ ํํํ๋ ๊ฐ์ฒด๋ฅผ ํฌํจํด์ ๋ถ๋ถ ๊ฐ์ฒด์ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๋ค. ๋ํ, ํฉ์ฑ์์ ๋ ๊ฐ์ฒด ์ฌ์ด์ ์์กด์ฑ์ ๋ฐํ์์ ํด๊ฒฐ๋๋ค.
- ์์ ๊ด๊ณ๋ is-a ๊ด๊ณ๋ผ๊ณ ๋ถ๋ฅด๊ณ ํฉ์ฑ ๊ด๊ณ๋ has-a ๊ด๊ณ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
- ํฉ์ฑ์ ๊ตฌํ์ ์์กดํ์ง ์๋๋ค๋ ์ ์์ ์์๊ณผ ๋ค๋ฅด๋ค. ํฉ์ฑ์ ๋ด๋ถ์ ํฌํจ๋๋ ๊ฐ์ฒด์ ๊ตฌํ์ด ์๋ ํผ๋ธ๋ฆญ ์ธํฐํ์ด์ค์ ์์กดํ๋ค. ๋ฐ๋ผ์ ํฉ์ฑ์ ์ด์ฉํ๋ฉด ํฌํจ๋ ๊ฐ์ฒด์ ๋ด๋ถ ๊ตฌํ์ด ๋ณ๊ฒฝ๋๋๋ผ๋ ์ํฅ์ ์ต์ํํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ์ ๋ ์์ ์ ์ธ ์ฝ๋๋ฅผ ์ป์ ์ ์๊ฒ ๋๋ค.
- ์ฝ๋ ์์ฑ ์์ ์ ๊ฒฐ์ ํ ์์ ๊ด๊ณ๋ ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ์ง๋ง ํฉ์ฑ ๊ด๊ณ๋ ์คํ ์์ ์ ๋์ ์ผ๋ก ๋ณ๊ฒฝํ ์ ์๋ค.
์ฝ๋ ์ฌ์ฌ์ฉ์ ์ํด์๋ ๊ฐ์ฒด ํฉ์ฑ์ด ํด๋์ค ์์๋ณด๋ค ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ค.
- ์์์ ๋ถ๋ชจ ํด๋์ค ์์ ๊ตฌํ๋ ์ฝ๋ ์์ฒด๋ฅผ ์ฌ์ฌ์ฉํ์ง๋ง ํฉ์ฑ์ ํฌํจ๋๋ ๊ฐ์ฒด์ ํผ๋ธ๋ฆญ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฌ์ฉํ๋ค.
๐ ์์์ ํฉ์ฑ์ผ๋ก ๋ณ๊ฒฝํ๊ธฐโ
- 10์ฅ์์ ์ฝ๋ ์ฌ์ฌ์ฉ์ ์ํด ์์์ ๋จ์ฉํ์ ๋ ์ธ ๊ฐ์ง ๋ฌธ์ ์ ์ ๋ถํ์ํ ์ธํฐํ์ด์ค ์์ ๋ฌธ์ , ๋ฉ์๋ ์ค๋ฒ๋ผ์ด๋ฉ ์ค์์ฉ ๋ฌธ์ , ๋ถ๋ชจ ํด๋์ค์ ์์ ํด๋์ค์ ๋์ ์์ ๋ฌธ์ ๊ฐ ์กด์ฌํ๋ค.
- ํฉ์ฑ์ ์ฌ์ฉํ๋ฉด ์์์ด ์ด๋ํ๋ ์ธ ๊ฐ์ง ๋ฌธ์ ์ ์ ํด๊ฒฐํ ์ ์๋ค.
- 10์ฅ์ ์์ ์์ ๋ฅผ ํฉ์ฑ ๊ด๊ณ๋ก ๋ณ๊ฒฝํด๋ณธ๋ค.
๐ ๋ถํ์ํ ์ธํฐํ์ด์ค ์์ ๋ฌธ์ : java.util.Properties์ java.util.Stackโ
Vector
๋ฅผ ์์๋ฐ๋Stack
์Vector
์ ์ธ์คํด์ค ๋ณ์๋ฅผStack
ํด๋์ค์ ์ธ์คํด์ค ๋ณ์๋ก ์ ์ธํจ์ผ๋ก์จ ํฉ์ฑ ๊ด๊ณ๋ก ๋ณ๊ฒฝํ ์ ์๋ค.
public class Stack<E> {
private Vector<E> elements = new Vector()<>;
public E push(E item) {
elements.addElement(item);
return item;
}
public E pop() {
if (elements.isEmpty()) {
throw new EmptyStackException();
}
return elements.remove(elements.size() - 1);
}
}
๐ ๋ฉ์๋ ์ค๋ฒ๋ผ์ด๋ฉ์ ์ค์์ฉ ๋ฌธ์ : InstrumentHashSetโ
InstrumentHashSet
๋ ๊ฐ์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํด์ ํฉ์ฑ ๊ด๊ณ๋ก ๋ณ๊ฒฝํ ์ ์๋ค.HashSet
์ธ์คํด์ค๋ฅผ ๋ด๋ถ์ ํฌํจํ ํHashSet
์ ํผ๋ธ๋ฆญ ์ธํฐํ์ด์ค์์ ์ ๊ณตํ๋ ์คํผ๋ ์ด์ ๋ค์ ์ด์ฉํด ํ์ํ ๊ธฐ๋ฅ์ ๊ตฌํํ๋ฉด ๋๋ค.
public class InstrumentHashSet<E> implements Set<E> {
private int addCount = 0;
private Set<E> set;
public InstrumentHashSet(Set<E> set) {
this.set = set;
}
@Override
public boolean add(E e) {
addCount++;
return set.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return set.addAll(c);
}
public int getAddCount() {
return addCount;
}
@Override public boolean remove(Object o) { return set.remove(o); }
@Override public void clear() { set.clear(); }
// ...
}
InstrumentHashSet
์HashSet
์ด ์ ๊ณตํ๋ ๋ฉ์๋๋ฅผ ๊ทธ๋๋ก ์ ๊ณตํด์ผ ํ๋ค.HashSet
์Set
์ธํฐํ์ด์ค๋ฅผ ์ค์ฒดํํ๋ ๊ตฌํ์ฒด ์ค ํ๋์ด๋ฉฐ,InstrumentHashSet
์ด ์ ๊ณตํด์ผ ํ๋ ๋ชจ๋ ์คํผ๋ ์ด์ ๋ค์Set
์ธํฐํ์ด์ค์ ์ ์๋ผ ์๋ค. ๋ฐ๋ผ์InstrumentHashSet
์ดSet
์ธํฐํ์ด์ค๋ฅผ ์ค์ฒดํํ๋ฉด์ ๋ด๋ถ์HashSet
์ ์ธ์คํด์ค๋ฅผ ํฉ์ฑํ๋ฉดHashSet
์ ๋ํ ๊ตฌํ ๊ฒฐํฉ๋๋ ์ ๊ฑฐํ๋ฉด์๋ ํผ๋ธ๋ฆญ ์ธํฐํ์ด์ค๋ ๊ทธ๋๋ก ์ ์งํ ์ ์๋ค. ์ด๋ฅผ ํฌ์๋ฉ(forwarding)์ด๋ผ๊ณ ๋ถ๋ฅด๊ณ ๋์ผํ ๋ฉ์๋๋ฅผ ํธ์ถํ๊ธฐ ์ํด ์ถ๊ฐ๋ ๋ฉ์๋๋ฅผ ํฌ์๋ฉ ๋ฉ์๋(forwarding method)๋ผ๊ณ ๋ถ๋ฅธ๋ค.
๐ ๋ถ๋ชจ ํด๋์ค์ ์์ ํด๋์ค์ ๋์ ์์ ๋ฌธ ์ : PersonalPlaylistโ
- ์ด๊ฒฝ์ฐ์ ํฉ์ฑ์ ์ด์ฉํ์ฌ ๋ณ๊ฒฝํ๋๋ผ๋ ๋ ํด๋์ค์์ ๋ณ๊ฒฝ์ด ์ผ์ด๋๋ค.
public class PersonalPlaylist {
private Playlist playlist = new Playlist();
public void append(Song song) {
// ...
}
public void remove(Song song) {
// ...
}
}
- ๊ทธ๋ ๋ค๊ณ ํ๋๋ผ๋ ์ฌ์ ํ ์์๋ณด๋ค๋ ํฉ์ฑ์ ์ฌ์ฉํ๋ ๊ฒ์ด ๋ ์ข์๋ฐ ํฅํ์
Playlist
์ ๋ด๋ถ ๊ตฌํ์ ๋ณ๊ฒฝํ๋๋ผ๋ ํ๊ธํจ๊ณผ๋ฅผ ์ต๋ํPersonalPlaylist
๋ด๋ถ๋ก ์บก์ํํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
- ๋ชฝํค ํจ์น
๋ชฝํค ํจ์น(Monkey Patch)๋ ํ์ฌ ์คํ ์ค์ธ ํ๊ฒฝ์๋ง ์ํฅ์ ๋ฏธ์น๋๋ก ์ง์ญ์ ์ผ๋ก ์ฝ๋๋ฅผ ์์ ํ๊ฑฐ๋ ํ์ฅํ๋ ๊ฒ์ ๊ฐ๋ฆฌํจ๋ค.
Playlist
์ ์ฝ๋๋ฅผ ์์ ํ ๊ถํ์ด ์๊ฑฐ๋ ์์ค์ฝ๋๊ฐ ์กด์ฌํ์ง ์๋๋ค๊ณ ํ๋๋ผ๋ ๋ชฝํค ํจ์น๊ฐ ์ง์๋๋ ํ๊ฒฝ์ด๋ผ๋ฉดPlaylist
์ ์ง์ remove
๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค. (๋ฃจ๋น์ ์ด๋ฆฐ ํด๋์ค, C#์ ํ์ฅ ๋ฉ์๋)
- ํฉ์ฑ์ ์์ ์ฑ๊ณผ ์ ์ฐ์ฑ์ด๋ผ๋ ์ฅ์ ์ ์ ๊ณตํ๋ค.
๐ ์์์ผ๋ก ์ธํ ์กฐํฉ์ ํญ๋ฐ์ ์ธ ์ฆ๊ฐโ
- ์์์ผ๋ก ์ธํด ๊ฒฐํฉ๋๊ฐ ๋์์ง๋ฉด ์ฝ๋๋ฅผ ์์ ํ๋ ๋ฐ ํ์ํ ์์
์ ์์ด ๊ณผ๋ํ๊ฒ ๋์ด๋๋ ๊ฒฝํฅ์ด ์๋ค. (์์ ๊ธฐ๋ฅ๋ค์ ์กฐํฉํด์ ๋ ํฐ ๊ธฐ๋ฅ์ ์ํํ๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ผ ํ๋ ๊ฒฝ์ฐ)
- ํ๋์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฑฐ๋ ์์ ํ๊ธฐ ์ํด ๋ถํ์ํ๊ฒ ๋ง์ ์์ ํด๋์ค๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์์ ํด์ผ ํ๋ค.
- ๋จ์ผ ์์๋ง ์ง์ํ๋ ์ธ์ด์์๋ ์์์ผ๋ก ์ธํด ์คํ๋ ค ์ค๋ณต ์ฝ๋์ ์์ด ๋์ด๋ ์ ์๋ค.
๐ ๊ธฐ๋ณธ ์ ์ฑ ๊ณผ ๋ถ๊ฐ ์ ์ฑ ์กฐํฉํ๊ธฐโ
- 10์ฅ์์ ์๊ฐํ๋ ํธ๋ํฐ ๊ณผ๊ธ ์์คํ ์ ์๋ก์ด ์๊ตฌ์ฌํญ์ ์ถ๊ฐํด๋ณธ๋ค.
- ์๋ก์ด ์๊ตฌ์ฌํญ์ ์ผ๋ฐ ์๊ธ์ ์ ์ฌ์ผ ํ ์ธ ์๊ธ์ ๋ผ๋ ๋ ์๊ธ์ ์ ๋ถ๊ฐ ์ ์ฑ ์ ์ถ๊ฐํ๋ ๊ฒ์ด๋ค. (๊ธฐ๋ณธ ์ ์ฑ ๊ณผ ๋ถ๊ฐ ์ ์ฑ )
- ๋ถ๊ฐ ์ ์ฑ ์ ํตํ๋๊ณผ ๋ฌด๊ดํ๊ฒ ๊ธฐ๋ณธ ์ ์ฑ ์ ์ ํ์ ์ผ๋ก ์ถ๊ฐํ ์ ์๋ ์๊ธ ๋ฐฉ์.
- ๋๋ฌธ์ ๋ถ๊ฐ ์ ใ ์ฑ ์ ๊ธฐ๋ณธ ์ ์ฑ ์ ๊ณ์ฐ ๊ฒฐ๊ณผ์ ์ ์ฉ๋ ์ ์๊ณ ์ ํ์ ์ผ๋ก ์ ์ฉํ ์ ์์ผ๋ฉฐ ์กฐํฉ์ด ๊ฐ๋ฅํ๋ค. ๋ํ, ๋ถ๊ฐ ์ ์ฑ ์ ์์์ ์์๋ก ์ ์ฉ ๊ฐ๋ฅํ๋ค.
- ๊ธฐ๋ณธ ์ ์ฑ : ์ผ๋ฐ ์๊ธ์ , ์ฌ์ผ ํ ์ธ ์๊ธ์
- ๋ถ๊ฐ ์ ์ฑ : ์ธ๊ธ ์ ์ฑ , ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ
๐ ์์์ ์ด์ฉํด์ ๊ธฐ๋ณธ ์ ์ฑ ๊ตฌํํ๊ธฐโ
- 10์ฅ์ ์์ ์ ๊ฐ์.
๐ ๊ธฐ๋ณธ ์ ์ฑ ์ ์ธ๊ธ ์ ์ฑ ์กฐํฉํ๊ธฐโ
- ๋ค์์ ์ผ๋ฐ ์๊ธ์ ์ ์ธ๊ธ ์ ์ฑ
์ ์กฐํฉํ๋ ๊ฒ์ผ๋ก
RegularPhone
ํด๋์ค๋ฅผ ์์๋ฐ์TaxableRegularPhone
ํด๋์ค๋ฅผ ์ถ๊ฐํ ๊ฒ์ด๋ค.
public class TaxableRegularPhone extends RegularPhone {
private double taxRate;
public TaxableRegularPhone(Money amount, Duration seconds, double taxRate) {
super(amount, seconds);
this.taxRate = taxRate;
}
@Override
public Money calculateFee() { // ๋ถ๋ชจ ํด๋์ค์์ ์ค๋ฒ๋ผ์ด๋ฉ
Money fee = super.calculateFee();
return fee.plus(fee.times(taxRate)); // ์ธ๊ธ ์ ์ฑ
์ ์กฐํฉํ ์๊ธ์ ๊ณ์ฐ
}
}
super
ํธ์ถ์ ์ฌ์ฉํ๋ค๋ฉด ์ํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒ ์ป์ ์๋ ์์ง๋ง ์์ ํด๋์ค์ ๋ถ๋ชจ ํด๋์ค ์ฌ์ด์ ๊ฒฐํฉ๋๊ฐ ๋์์ง๊ณ ๋ง๋ค. ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๋ ๋ฐฉ๋ฒ์ ์์ ํด๋์ค๊ฐ ๋ถ๋ชจ ํด๋์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์๋๋ก ๋ถ๋ชจ ํด๋์ค์ ์ถ์ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด๋ค.
public abstract class Phone {
private List<Call> calls = new ArrayList<>();
public Money calculateFee() {
Money result = Money.ZERO;
for(Call call : calls) {
result = result.plus(calculateCallFee(call));
}
return afterCalculated(result);
}
protected abstract Money calculateCallFee(Call call);
protected abstract Money afterCalculated(Money fee);
}
- ์์ ํด๋์ค๋
afterCalculated
๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ ๊ณ์ฐ๋ ์๊ธ์ ์ ์ฉํ ์์ ์ ์ถ๊ฐํ๋ค.
public class RegularPhone extends Phone {
// ...
@Override
protected Money calculateCallFee(Call call) {
return amount.times(call.getDuration().getSeconds() / seconds.getSeconds());
}
@Override
protected Money afterCalculated(Money fee) {
return fee;
}
}
NightlyDiscountPhone
ํด๋์ค ์ญ์ ์์ ํด์ผ ํ๋ค.
public class NightlyDiscountPhone extends Phone {
// ...
@Override
protected Money calculateCallFee(Call call) {
if(call.getFrom().getHour() >= LATE_NIGHT_HOUR) {
return nightlyAmount.times(call.getDuration().getSeconds() / seconds.getSeconds());
} else {
return regularAmount.times(call.getDuration().getSeconds() / seconds.getSeconds());
}
}
@Override
protected Money afterCalculated(Money fee) {
return fee;
}
}
- ์ด์ฒ๋ผ ๋ถ๋ชจ ํด๋์ค์ ์ถ์ ๋ฉ์๋๋ฅผ ์ถ๊ฐํ๋ฉด ๋ชจ๋ ์์ ํด๋์ค๋ค์ด ์ถ์ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ผํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
- ๋ชจ๋ ์ถ์ ๋ฉ์๋์ ๊ตฌํ์ด ๋์ผํ๋ค. ์ ์ฐ์ฑ์ ์ ์งํ๋ฉด์๋ ์ค๋ณต ์ฝ๋๋ฅผ ์ ๊ฑฐํ ์ ์๋ ๋ฐฉ๋ฒ์
Phone
์์afterCalculated
๋ฉ์๋์ ๋ํ ๊ธฐ๋ณธ ๊ตฌํ์ ํจ๊ป ์ ๊ณตํ๋ ๊ฒ์ด๋ค. - ์ถ์ ๋ฉ์๋์ ๋จ์ ์ ์์ ๊ณ์ธต์ ์ํ๋ ๋ชจ๋ ์์ ํด๋์ค๊ฐ ์ถ์ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํด์ผ ํ๋ค๋ ๊ฒ์ด๋ค.
public abstract class Phone {
// ...
protected Money afterCalculated(Money fee) { // ํ
๋ฉ์๋
return fee;
}
protected abstract Money calculateCallFee(Call call);
}
-
์ด์ฒ๋ผ ์ถ์ ๋ฉ์๋์ ๋์ผํ๊ฒ ์์ ํด๋์ค์์ ์ค๋ฒ๋ผ์ด๋ฉํ ์๋๋ก ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์ง๋ง ํธ์๋ฅผ ์ํด ๊ธฐ๋ณธ ๊ตฌํ์ ์ ๊ณตํ๋ ๋ฉ์๋๋ฅผ ํ ๋ฉ์๋(hook method)๋ผ๊ณ ๋ถ๋ฅธ๋ค.
-
TaxableRegularPhone
์ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ๋ค.
public class TaxableRegularPhone extends RegularPhone {
private double taxRate;
public TaxableRegularPhone(Money amount, Duration seconds, double taxRate) {
super(amount, seconds);
this.taxRate = taxRate;
}
@Override
public Money afterCalculated(Money fee) {
return fee.plus(fee.times(taxRate));
}
- ์ฌ์ผ ํ ์ธ ์๊ธ์ ์ธ
NightDiscountPhone
์๋ ์ธ๊ธ์ ๋ถ๊ณผํ ์ ์๋๋กTaxableNightDiscountPhone
์ ์ถ๊ฐํ๋ค.
public class TaxableNightDiscountPhone extends NightlyDiscountPhone {
private double taxRate;
public TaxableNightDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, double taxRate) {
super(nightlyAmount, regularAmount, seconds);
this.taxRate = taxRate;
}
@Override
public Money afterCalculated(Money fee) {
return fee.plus(fee.times(taxRate));
}
- ๋ฌธ์ ๋
TaxableNightDiscountPhone
๊ณผTaxableRegularPhone
์ฌ์ด์ ์ฝ๋๋ฅผ ์ค๋ณตํ๋ค๋ ๊ฒ์ด๋ค. ๋ ํด๋์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ถ๋ชจ ํด๋์ค์ ์ด๋ฆ์ ์ ์ธํ๋ฉด ๋๋ถ๋ถ ์ฝ๋๊ฐ ๊ฑฐ์ ๋์ผํ๋ค. - ๋๋ถ๋ถ์ ๊ฐ์ฒด์งํฅ ์ธ์ด๋ ๋จ์ผ ์์๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์ ์์์ผ๋ก ์ธํด ๋ฐ์ํ๋ ์ค๋ณต ์ฝ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ๊ฐ ์ฝ์ง ์๋ค.