๐ 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์ฌ์ด์ ์ฝ๋๋ฅผ ์ค๋ณตํ๋ค๋ ๊ฒ์ด๋ค. ๋ ํด๋์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ถ๋ชจ ํด๋์ค์ ์ด๋ฆ์ ์ ์ธํ๋ฉด ๋๋ถ๋ถ ์ฝ๋๊ฐ ๊ฑฐ์ ๋์ผํ๋ค. - ๋๋ถ๋ถ์ ๊ฐ์ฒด์งํฅ ์ธ์ด๋ ๋จ์ผ ์์๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์ ์์์ผ๋ก ์ธํด ๋ฐ์ํ๋ ์ค๋ณต ์ฝ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ๊ฐ ์ฝ์ง ์๋ค.