๐ 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
์ฌ์ด์ ์ฝ๋๋ฅผ ์ค๋ณตํ๋ค๋ ๊ฒ์ด๋ค. ๋ ํด๋์ค์ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ถ๋ชจ ํด๋์ค์ ์ด๋ฆ์ ์ ์ธํ๋ฉด ๋๋ถ๋ถ ์ฝ๋๊ฐ ๊ฑฐ์ ๋์ผํ๋ค. - ๋๋ถ๋ถ์ ๊ฐ์ฒด ์งํฅ ์ธ์ด๋ ๋จ์ผ ์์๋ง ์ง์ํ๊ธฐ ๋๋ฌธ์ ์์์ผ๋ก ์ธํด ๋ฐ์ํ๋ ์ค๋ณต ์ฝ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ๊ฐ ์ฝ์ง ์๋ค.
๐ ๊ธฐ๋ณธ ์ ์ฑ ์ ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ ์กฐํฉํ๊ธฐโ
- ๋ ๋ฒ์งธ ๋ถ๊ฐ ์ ์ฑ
์ธ ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ
์
Phone
์ ์์ ๊ณ์ธต์ ์ถ๊ฐํด๋ณธ๋ค. - ์ผ๋ฐ ์๊ธ์ ์ ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ
์ ์กฐํฉํ๊ณ ์ถ์ผ๋ฉด
RegularPhone
์ ์์๋ฐ์RateDiscountableRegularPhone
ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ค.
public class RateDiscountableRegularPhone extends RegularPhone {
private Money discountAmount;
public RateDiscountableRegularPhone(Money amount, Duration seconds, Money discountAmount) {
super(amount, seconds);
this.discountAmount = discountAmount;
}
@Override
public Money afterCalculated(Money fee) {
return fee.minus(discountAmount);
}
- ์ฌ์ผ ํ ์ธ ์๊ธ์ ์ ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ
์ ์กฐํฉํ๊ณ ์ถ์ผ๋ฉด
NightlyDiscountPhone
์ ์์๋ฐ๋RateDiscountableNightlyDiscountPhone
ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.
public class RateDiscountableNightlyDiscountPhone extends NightlyDiscountPhone {
private Money discountAmount;
public RateDiscountableNightlyDiscountPhone(Money nightlyAmount, Money regularAmount, Duration seconds, Money discountAmount) {
super(nightlyAmount, regularAmount, seconds);
this.discountAmount = discountAmount;
}
@Override
public Money afterCalculated(Money fee) {
return fee.minus(discountAmount);
}
- ์ด๋ฒ์๋ ๋ถ๊ฐ ์ ์ฑ
์ ๊ตฌํํ
RateDiscountableNightlyDiscountPhone
ํด๋์ค์RateDiscountableRegularPhone
ํด๋์ค ์ฌ์ด์ ์ค๋ณต ์ฝ๋๋ฅผ ์ถ๊ฐํ๋ค.
๐ ์ค๋ณต ์ฝ๋์ ๋ซ์ ๊ฑธ๋ฆฌ๋คโ
- ๋ถ๊ฐ ์ ์ฑ ์ ์์ ๋กญ๊ฒ ์กฐํฉํ ์ ์์ด์ผ ํ๊ณ ์ ์ฉ๋๋ ์์ ์ญ์ ์์๋ก ๊ฒฐ์ ํ ์ ์์ด์ผ ํ๋ค.
- ์์์ ์ด์ฉํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ชจ๋ ๊ฐ๋ฅํ ์กฐํฉ๋ณ๋ก ์์ ํด๋์ค๋ฅผ ํ๋์ฉ ์ถ๊ฐํ๋ ๊ฒ์ด๋ค. ๊ต์ฅํ ๋ณต์กํด์ง ๊ฒ์ด๋ค.
- ํ์ง๋ง ๋ณต์ก์ฑ๋ณด๋ค ๋ ํฐ ๋ฌธ์ ๋ ๋ฐ๋ก ์๋ก์ด ์ ์ฑ ์ ์ถ๊ฐํ๊ธฐ๊ฐ ์ด๋ ต๋ค๋ ๊ฒ์ด๋ค.
- ํ์ฌ์ ์ค๊ณ์ ์๋ก์ด ์ ์ฑ ์ ์ถ๊ฐํ๊ธฐ ์ํด์๋ ๋ถํ์ํ ๋ง์ ์์ ํด๋์ค๋ฅผ ์์ ๊ณ์ธต ์์ ์ถ๊ฐํด์ผ ํ๋ค.
- ์์์ ๋จ์ฉ์ผ๋ก ํ๋์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ ์ํด ํ์ ์ด์์ผ๋ก ๋ง์ ์์ ํด๋์ค๋ฅผ ์ถ๊ฐํด์ผํ๋ ๊ฒฝ์ฐ๋ฅผ ๊ฐ๋ฆฌ์ผ ํด๋์ค ํญ๋ฐ(class explosion) ๋ฌธ์ ๋๋ ์กฐํฉ์ ํญ๋ฐ(combinational explosion) ๋ฌธ์ ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
- ํด๋์ค ํญ๋ฐ ๋ฌธ์ ๋ ์์ ํด๋์ค๊ฐ ๋ถ๋ชจ ํด๋์ค์ ๊ตฌํ์ ๊ฐํ๊ฒ ๊ฒฐํฉ๋๋๋ก ๊ฐ์ํ๋ ์์์ ๊ทผ๋ณธ์ ์ธ ํ๊ณ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค. ์ปดํ์ผํ์์ ๊ฒฐ์ ๋ ์์ ํด๋์ค์ ๋ถ๋ชจ ํด๋์ค ์ฌ์ด์ ๊ด๊ณ๋ ๋ณ๊ฒฝ๋ ์ ์๊ธฐ ๋๋ฌธ์ ์์ ํด๋์ค์ ๋ถ๋ชจ ํด๋์ค์ ๋ค์ํ ์กฐํฉ์ด ํ์ํ ์ํฉ์์ ์ ์ผํ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์กฐํฉ์ ์๋งํผ ์๋ก์ด ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ ๊ฒ๋ฟ์ด๋ค.
- ํด๋์ค ํญ๋ฐ ๋ฌธ์ ๋ ์๋ก์ด ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋๋ฟ๋ง ์๋๋ผ ๊ธฐ๋ฅ์ ์์ ํ ๋๋ ๋ฌธ์ ๊ฐ ๋๋ค. ๋ง์ฝ ์ธ๊ธ ์ ์ฑ ์ด ๋ณ๊ฒฝ๋๋ค๋ฉด ๊ด๋ จ๋ ์ฝ๋๊ฐ ์ฌ๋ฌ ํด๋์ค ์์ ์ค๋ณต๋ผ ์๊ธฐ ๋๋ฌธ์ ๋ชจ๋ ์ฐพ์ ๋์ผํ ๋ฐฉ์์ผ๋ก ์์ ํด์ผ ํ๋ค.
- ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ ์ต์ ์ ๋ฐฉ๋ฒ์ ์์์ ํฌ๊ธฐํ๋ ๊ฒ์ด๋ค.
๐ ํฉ์ฑ ๊ด๊ณ๋ก ๋ณ๊ฒฝํ๊ธฐโ
- ํฉ์ฑ์ ์ปดํ์ผํ์ ๊ด๊ณ๋ฅผ ๋ฐํ์ ๊ด๊ณ๋ก ๋ณ๊ฒฝํจ์ผ๋ก์จ ์์์ ํด๋์ค ํญ๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ค. ํฉ์ฑ์ ์ฌ์ฉํ๋ฉด ๊ตฌํ์ด ์๋ ํผ๋ธ๋ฆญ ์ธํฐํ์ด์ค์ ๋ํด์๋ง ์์กดํ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐํ ์์ ๊ฐ์ฒด์ ๊ด๊ณ๋ฅผ ๋ณ๊ฒฝํ ์ ์๋ค.
- ํฉ์ฑ ๊ด๊ณ๋ ๋ฐํ์์ ๋์ ์ผ๋ก ๋ณ๊ฒฝํ ์ ์๋ค. ํฉ์ฑ์ ์ฌ์ฉํ๋ฉด ์ปดํ์ผํ์ ์์กด์ฑ๊ณผ ๋ฐํ์ ์์กด์ฑ์ ๋ค๋ฅด๊ฒ ๋ง๋ค ์ ์๋ค.
- ์์์ด ์กฐํฉ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ๋ณ ํด๋์ค ์์ผ๋ก ๋ฐ์ด ๋ฃ๋ ๋ฐฉ๋ฒ์ด๋ผ๋ฉด ํฉ์ฑ์ ์กฐํฉ์ ๊ตฌ์ฑํ๋ ์์๋ค์ ๊ฐ๋ณ ํด๋์ค๋ก ๊ตฌํํ ํ ์คํ ์์ ์ ์ธ์คํด์ค๋ฅผ ์กฐ๋ฆฝํ๋ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
๐ ๊ธฐ๋ณธ ์ ์ฑ ํฉ์ฑํ๊ธฐโ
- ๋จผ์ ๊ธฐ๋ณธ ์ ์ฑ
๊ณผ ๋ถ๊ฐ ์ ์ฑ
์ ํฌ๊ดํ๋
RatePolicy
์ธํฐํ์ด์ค๋ฅผ ์ถ๊ฐํ๋ค.RatePolicy
๋Phone
์ ์ธ์๋ก ๋ฐ์ ๊ณ์ฐ๋ ์๊ธ์ ๋ฐํํ๋calculateFee
์คํผ๋ ์ด์ ์ ํฌํจํ๋ ๊ฐ๋จํ ์ธํฐํ์ด์ค์ด๋ค.
public interface RatePolicy {
Money calculateFee(Phone phone);
}
- ๊ธฐ๋ณธ ์ ์ฑ
์ ๊ตฌ์ฑํ๋ ์ผ๋ฐ ์๊ธ์ ์ ์ฌ์ผ ํ ์ธ ์๊ธ์ ๋ ๊ฐ๋ณ ์๊ธ์ ๊ณ์ฐํ๋ ๋ฐฉ์์ ์ ์ธํ ์ ์ฒด ๋ก์ง์ด ๊ฑฐ์ ๋์ผํ๋ค. ์ด ์ค๋ณต ์ฝ๋๋ฅผ ๋ด์ ์ถ์ ํด๋์ค
BasicRatePolicy
๋ฅผ ์ถ๊ฐํ๋ค.
public abstract class BasicRatePolicy implements RatePolicy {
@Override
public Money calculateFee(Phone phone) {
Money result = Money.ZERO;
for(Call call : phone.getCalls()) {
result.plus(calculateCallFee(call));
}
return result;
}
protected abstract Money calculateCallFee(Call call);
}
- ๋ค์์ ์ผ๋ฐ ์๊ธ์ ์
RegularPolicy
ํด๋์ค์ด๋ค.
public class RegularPolicy extends BasicRatePolicy {
// ...
@Override
protected Money calculateCallFee(Call call) {
return amount.times(call.getDuration().getSeconds() / seconds.getSeconds());
}
}
- ๋ค์์ ์ฌ์ผ ํ ์ธ ์๊ธ์ ์ธ
NightlyDiscountPolicy
ํด๋์ค์ด๋ค.
public class NightlyDiscountPolicy extends BasicRatePolicy {
// ...
@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());
}
}
}
- ์ด์ ๊ธฐ๋ณธ ์ ์ฑ
์ ์ด์ฉํด ์๊ธ์ ๊ณ์ฐํ ์ ์๋๋ก
Phone
์ ์์ ํ๋ค.
public class Phone {
private RatePolicy ratePolicy;
private List<Call> calls = new ArrayList<>();
public Phone(RatePolicy ratePolicy) {
this.ratePolicy = ratePolicy;
}
public List<Call> getCalls() {
return Collection.unmodifiableList(calls);
}
public Money calculateFee() {
return ratePolicy.calculateFee(this);
}
}
- ์ด๋
Phone
๋ด๋ถ์RatePolicy
์ ๋ํ ์ฐธ์กฐ์๊ฐ ํฌํจ๋ผ ์๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ํฉ์ฑ์ด๋ค.RatePolicy
๋ ์ธํฐํ์ด์ค์ด๋ค.Phone
์ ๋ฐํ์ ์์กด์ฑ์ผ๋ก ๋์ฒดํ๊ธฐ ์ํด ์์ฑ์๋ฅผ ํตํดRatePolicy
์ ์ธ์คํด์ค์ ๋ํ ์์กด์ฑ์ ์ฃผ์ ๋ฐ๋๋ค.
// ์ผ๋ฐ ์๊ธ์
Phone phone = new Phone(new RegularPolicy(Money.wons(10), Duration.ofSeconds(10)));
// ์ฌ์ผ ํ ์ธ ์๊ธ์
Phone phone = new Phone(new NightlyDiscountPolicy(
Money.wons(5), Money.wons(10), Duration.ofSeconds(10)
));
๐ ๋ถ๊ฐ ์ ์ฑ ์ ์ฉํ๊ธฐโ
- ๋ถ๊ฐ ์ ์ฑ
์
RatePolicy
์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๋ฉฐ, ๋ด๋ถ์ ๋ ๋ค๋ฅธRatePolicy
์ธ์คํด์ค๋ฅผ ํฉ์ฑํ ์ ์์ด์ผ ํ๋ค.
public abstract class AdditionalRatePolicy implements RatePolicy {
private RatePolicy next;
public AdditionalRatePolicy(RatePolicy next) {
this.next = next;
}
@Override
public Money calculateFee(Phone phone) {
Money fee = next.calculateFee(phone);
return afterCalculated(fee);
}
abstract protected Money afterCalculated(Money fee);
}
- ์ธ๊ธ ์ ์ฑ ๊ตฌํ
public class TaxablePolicy extends AdditionalRatePolicy {
private double taxRatio;
public TaxablePolicy(double taxRatio, RatePolicy next) {
super(next);
this.taxRatio = taxRatio;
}
@Override
protected Money afterCalculated(Money fee) {
return fee.plus(fee.times(taxRatio));
}
}
- ๊ธฐ๋ณธ ์๊ธ ํ ์ธ ์ ์ฑ
public class RateDiscountablePolicy extends AdditionalRatePolicy {
private Money discountAmount;
public TaxablePolicy(Money discountAmount, RatePolicy next) {
super(next);
this.discountAmount = discountAmount;
}
@Override
protected Money afterCalculated(Money fee) {
return fee.minus(fee.times(taxRatio));
}
}