π‘ μν ν¨ν΄ , μ€ν μ΄νΈ ν¨ν΄ (state pattern)
κ°μ²΄μ λ΄λΆ μνκ° λ°λμ λ°λΌμ κ°μ²΄μ νλμ λ°κΏ μ μλ€. λ§μΉ κ°μ²΄μ ν΄λμ€κ° λ°λλ κ²κ³Ό κ°μ κ²°κ³Όλ₯Ό μ»μ μ μλ€.
β μμ : λ½κΈ° μνκΈ°
(1) μꡬμ¬ν
"λ€μκ³Ό κ°μ λ°©μμΌλ‘ μλνλ λ½κΈ° κΈ°κ³μ μλ°μ½λλ₯Ό μμ±ν΄μ£ΌμΈμ. λμ€μ λ€λ₯Έ κΈ°λ₯μ μΆκ°ν κ°λ₯μ±λ μμΌλ μ΅λν μ μ°νκ³ κ΄λ¦¬νκΈ° μ©μ΄ν λμμΈμΌλ‘ ν΄μ€¬μΌλ©΄ ν©λλ€."
μν λ€μ΄μ΄κ·Έλ¨μ λ°νμΌλ‘ μνλ₯Ό μ μνμ.
- μνμ μ
- No Coin : λμ΄ λ£μ΄μ Έμμ§ μμ μν
- Has Coin : λ λ£μ΄μ§ μν
- Sold : ν맀μ€μ μν
- Sold Out : λ§€μ§ μν
(2) λ¬Έμ μΈμ
1. λ€μκ³Ό κ°μ΄ λ½κΈ° μνκΈ°λ₯Ό μμ±νλ©΄ λ κ² κ°λ€.
public class GachaMachine {
// κ° μνλ₯Ό μ μ₯νκΈ°μν μΈμ€ν΄μ€λ³μλ₯Ό λ§λ€μ΄ μ μ
final static int SOLD_OUT = 0; // 맀μ§
final static int NO_COIN = 1; // λμ μμ
final static int HAS_COIN = 2; // λμ μμ
final static int SOLD = 3; // ν맀
int state = SOLD_OUT; // νμ¬ μνλ₯Ό μ μ₯νλ μΈμ€ν΄μ€ λ³μ
int count = 0; // λ½κΈ° κ°―μ
public GachaMachine(int count) {
this.count = count;
if (count > 0) {
state = NO_COIN;
}
}
public void insertCoin() {
if (state == HAS_COIN) {
// ν맀λκΈ° μν : λμ O
System.out.println("λμ μ΄ μ΄λ―Έ μμ΄μ. λ λ£μ μ μμ΄μ.");
} else if (state == NO_COIN) {
// ν맀λκΈ° μν : λμ X
state = HAS_COIN;
System.out.println("λμ μ λ£μμ΅λλ€.");
} else if (state == SOLD_OUT) {
// λ§€μ§ μν
System.out.println("sold out! λ€ νλ Έμ΄μ. λ λ£μ§λ§μΈμ");
} else if (state == SOLD) {
// νλ§€μ€ μν
System.out.println("μ μλ§ κΈ°λ€λ €μ£ΌμΈμ. λ½κΈ°κ° λμ¬κ±°μμ");
}
}
public void ejectCoin() { // μ¬μ©μκ° λμ λ°ν νλ κ²½μ°
if (state == HAS_COIN) {
// ν맀λκΈ° : λμ O, λ°ν O
System.out.println("λμ μ΄ λ°νλ©λλ€.");
state = NO_COIN;
} else if (state == NO_COIN) {
// ν맀λκΈ° : λμ X, λ°ν X
System.out.println("λμ μ΄ μμ΄μ");
} else if (state == SOLD) {
// νλ§€μ€ : μ΄λ―Έ ν맀λμ΄ λ°ν X
System.out.println("μ΄λ―Έ ꡬμ
νμ
¨μ΄μ");
} else if (state == SOLD_OUT) {
// λ§€μ§ : λμ X, λ°ν X
System.out.println("λμ λ°ν μλΌμ. 맀μ§μΌλ‘ λλ μλ°μμ΄μ");
}
}
public void turnCrank() { // μ¬μ©μκ° μμ‘μ΄λ₯Ό λ리λ κ²½μ°
if (state == SOLD) {
System.out.println("μμ‘μ΄λ ν λ²λ§ λλ €μ£ΌμΈμ.");
} else if (state == NO_COIN) {
System.out.println("λμ μ λ£μ΄μ£ΌμΈμ");
} else if (state == SOLD_OUT) {
System.out.println("λ§€μ§ λμμ΅λλ€");
} else if (state == HAS_COIN) {
System.out.println("μμ‘μ΄λ₯Ό λλ Έμ΅λλ€. λ½κΈ°κ° λμ¬κ±°μμ");
state = SOLD;
dispense();
}
}
private void dispense() {
if (state == SOLD) {
System.out.println("λ½κΈ°κ° λκ°κ³ μμ΄μ");
count = count - 1;
if (count == 0) {
System.out.println("λ½κΈ° μ¬κ³ κ° μμ΄μ");
state = SOLD_OUT;
} else {
state = NO_COIN;
}
} else if (state == NO_COIN) {
System.out.println("λ¨Όμ λμ μ λ£μ΄μ£ΌμΈμ");
} else if (state == SOLD_OUT) {
System.out.println("λ§€μ§ μ
λλ€.");
} else if (state == HAS_COIN) {
System.out.println("λ½κΈ°κ° λκ° μ μμ΄μ.");
}
}
// μ΄ν μμ
public void refill(int numGumBalls) {
this.count = numGumBalls;
state = NO_COIN;
}
public String toString() {
StringBuffer result = new StringBuffer();
result.append("==== λ½κΈ° μνκΈ° ==== \n");
result.append("μ¬κ³ : " + count + " κ°");
result.append("\nνμ¬ μνκΈ° μνλ ");
if (state == SOLD_OUT) {
result.append("λ§€μ§ : sold out!!");
} else if (state == NO_COIN) {
result.append("ν맀λκΈ°(λ X) : λμ λ£μ΄μ£ΌμΈμ");
} else if (state == HAS_COIN) {
result.append("ν맀λκΈ°(λ O) : μμ‘μ΄λ₯Ό λλ €μ£ΌμΈμ");
} else if (state == SOLD) {
result.append("νλ§€μ€ : λ½κΈ°κ° λμ€κ³ μμ΅λλ€.");
}
result.append("\n");
return result.toString();
}
}
2. λ³κ²½μμ²μ΄ λ€μ΄μλ€. λ½κΈ°μ 1κ°λ₯Ό λ λ°μ μ μλ κΈ°λ₯μ μΆκ°ν΄μ£ΌμΈμ.
"Buy 1 Get 1 : λμ νμΈμ! 10λΆμ 1 νλ₯ !"
μμκ°μ μμ€ μμ±λ°©λ²μΌλ‘λ λ€μκ³Ό κ°μ΄ μμ±ν μ λ°μ μλ€.
final static int SOLD_OUT = 0;
final static int NO_COIN = 1;
final static int HAS_COIN = 2;
final static int SOLD = 3;
// 1) μν κ° μ μ₯μ μν μΈμ€ν΄μ€ λ³μ μ μΈ : WINNER μνλ₯Ό μΆκ°ν΄μΌνλ€.
// 2) κ° μν 쑰건문 : μΆκ°ν λλ§λ€ κ° λ©μλμ if()λ₯Ό μ΄μ©νμ¬ μ‘°κ±΄μ μΆκ°ν΄μΌ νλ€.
public void insertCoin () {
// λμ ν¬μ
μ ν΄μΌ ν μΌ : ν맀λκΈ°(λμ o)
}
public void ejectCoin () {
// λμ λ°νμ ν΄μΌ ν μΌ : λμ λ°νν ν맀λκΈ°(λμ x) μνμ€μ
}
public void turnCrank() {
// μμ‘μ΄λ₯Ό λλ Έμ λ ν΄μΌ ν μΌ
}
public void dispense() {
// λ½κΈ° λ΄λ³΄λΌλ ν΄μΌ ν μΌ : λ¨μ μ¬κ³ νμ
ν μνμ€μ
}
κ° μν κ° μ μ₯μ μν μΈμ€ν΄μ€ λ³μκ° μΆκ°λκ³ , κ° μνμ λν ifλ¬Έμ 쑰건μ μΆκ°ν΄μΌνλ€. μ°¨ν μꡬμ¬νμ΄ μκΈ°λ©΄ λ μΆκ°ν΄μΌνκ³ κ·Έ μκ° λ§μμ§λ©΄ μ§μ λΆν΄μ§λ€.
(3) λ¬Έμ μ
ꡬ체μ μΈ λ¬Έμ μ μ 리
- OCP μμΉμ μ§ν€μ§ μκ³ μλ€.
- κ°μ²΄μ§ν₯ λμμΈμ΄λΌκ³ νκΈ°μ λ¬΄λ¦¬κ° μλ€.
- μ§μ λ¬Έν 쑰건문μΌλ‘ μΈν΄ μνμ νν λΆλͺ νκ² λλ¬λμ§ μλλ€.
- λ€λ₯Έ κΈ°λ₯μ μΆκ°νλ κ³Όμ μμ κΈ°μ‘΄ μ½λμ μλ μλ‘μ΄ λ²κ·Έκ° λ°μν κ°λ₯μ±μ΄ ν¬λ€.
μ¦, κ° μνμ νλμ λ³λμ ν΄λμ€μ μ§μ λ£κ³ λͺ¨λ μνμμ κ°μ μκΈ°κ° ν μΌμ ꡬνν΄λ³΄μ.
λͺ¨λ μνλ₯Ό μΊ‘μνμμΌ State μΈν°νμ΄μ€λ₯Ό ꡬνν΄λ³΄μ.
- State μΈν°νμ΄μ€
- λͺ¨λ μν ν΄λμ€μμ μ¬μ©ν μΈν°νμ΄μ€
- λ©μλλ λ½κΈ° κΈ°κ³μμ μΌμ΄λ μ μλ λͺ¨λ νλλ€μ μ§μ μ μΌλ‘ λμλλ€.
- κ° μνλ₯Ό λνλ΄λ ꡬ체 ν΄λμ€
- κ° μνλ₯Ό μ§μ ν΄λμ€μ λμμμΌ μμ±νλ€.
β μλ‘μ΄ λμμΈ
- λ½κΈ° κΈ°κ³μ κ΄λ ¨λ λͺ¨λ νλμ λν λ©μλκ° λ€μ΄μλ State μΈν°νμ΄μ€λ₯Ό μ μνλ€.
- κΈ°κ³μ λͺ¨λ μνμ λνμ¬ μν ν΄λμ€λ₯Ό ꡬνν΄μΌ νλ€. κΈ°κ³κ° μ΄λ€ μνμ μλ€λ©΄, κ·Έ μνμ ν΄λΉνλ μν ν΄λμ€κ° λͺ¨λ μμ μ μ± μμ§λλ‘ νλ€.
- λ§μ§λ§μΌλ‘ 쑰건문 μ½λλ₯Ό μ λΆ μμ κ³ μν ν΄λμ€μ λͺ¨λ μμ μ μμνλ€.
1. State μΈν°νμ΄μ€
public interface State {
void insertCoin();
void ejectCoin();
void turnCrank();
void dispense();
void refill();
}
2. κ° μν ν΄λμ€ κ΅¬ν
/**
* ν맀λκΈ° μν μ 보 : λμ O
*/
public class HasCoinState implements State {
GachaMachine gachaMachine;
public HasCoinState(GachaMachine gachaMachine) {
this.gachaMachine = gachaMachine;
}
@Override
public void insertCoin() {
System.out.println("λμ μ λ£μμ μμ΄μ. μ΄λ―Έ λμ μ΄ μμ΅λλ€.");
}
@Override
public void ejectCoin() {
// λμ λ°ν
System.out.println("λμ μ λ°νν©λλ€.");
// λμ λ°ν ν λμ μμ μνλ‘ λ°κΏμ€λ€.
gachaMachine.setState(gachaMachine.getNoCoinState());
}
@Override
public void turnCrank() {
// μμ‘μ΄ λ리기
System.out.println("μμ‘μ΄λ₯Ό λ립λλ€.");
// μμ‘μ΄ λλ¦° κ²°κ³Ό : λ½κΈ° ν맀 μμ
gachaMachine.setState(gachaMachine.getSoldState());
}
@Override
public void dispense() {
System.out.println("λ½κΈ° μμ§ λμ€μ§ μμμ΄μ.");
}
@Override
public void refill() {
}
@Override
public String toString() {
return "ν맀λκΈ° μνμ
λλ€ : μμ‘μ΄λ₯Ό λλ €μΌ λ½κΈ°κ° λμ΅λλ€.";
}
}
/**
* ν맀λκΈ° μν μ 보 : λμ X
*/
public class NoCoinState implements State {
GachaMachine gachaMachine;
public NoCoinState(GachaMachine gachaMachine) {
this.gachaMachine = gachaMachine;
}
@Override
public void insertCoin() {
System.out.println("λμ μ λ£μ΅λλ€.");
gachaMachine.setState(gachaMachine.getHasCoinState());
}
@Override
public void ejectCoin() {
System.out.println("λμ μ΄ μμ΅λλ€. λμ μ λ°νν μ μμ΄μ.");
}
@Override
public void turnCrank() {
System.out.println("λμ μ΄ μμ΅λλ€. μμ‘μ΄λ₯Ό λλ €λ λ½κΈ°κ° λμ€μ§ μμ΅λλ€.");
}
@Override
public void dispense() {
System.out.println("λμ μ λ£μ΄μ£ΌμΈμ.");
}
@Override
public void refill() {
}
@Override
public String toString() {
return "ν맀λκΈ° μνμ
λλ€ : λμ μ λ£μ΄μ£ΌμΈμ.";
}
}
/**
* λ§€μ§ μν μ 보
*/
public class SoldOutState implements State {
GachaMachine gachaMachine;
public SoldOutState(GachaMachine gachaMachine) {
this.gachaMachine = gachaMachine;
}
@Override
public void insertCoin() {
System.out.println("맀μ§μ
λλ€. λμ μ λ£μ§λ§μΈμ");
}
@Override
public void ejectCoin() {
System.out.println("λ£μ λμ μ΄ μμ΅λλ€.");
}
@Override
public void turnCrank() {
System.out.println("λμ μ λ£μ΄μ£ΌμΈμ.");
}
@Override
public void dispense() {
System.out.println("λ½κΈ°κ° μμ΅λλ€.");
}
@Override
public void refill() {
gachaMachine.setState(gachaMachine.getNoCoinState());
}
@Override
public String toString() {
return "λ§€μ§ μνμ
λλ€. sold out!!";
}
}
/**
* νλ§€μ€ μν
*/
public class SoldState implements State {
GachaMachine gachaMachine;
public SoldState(GachaMachine gachaMachine) {
this.gachaMachine = gachaMachine;
}
@Override
public void insertCoin() {
System.out.println("μ μλ§ κΈ°λ€λ €μ£ΌμΈμ. μ§κΈ λ½κΈ°κ° λμ€κ³ μμ΅λλ€.");
}
@Override
public void ejectCoin() {
System.out.println("μ£μ‘ν©λλ€. μ΄λ―Έ λ½κΈ°κ° λμ€κ³ μμ΄ νλΆμ΄ λΆκ°ν©λλ€.");
}
@Override
public void turnCrank() {
System.out.println("μ μλ§ κΈ°λ€λ €μ£ΌμΈμ. μ΄λ―Έ λ½κΈ°κ° λμ€κ³ μμ΅λλ€.");
}
@Override
public void dispense() {
// λ½κΈ° ν맀 μ²λ¦¬
gachaMachine.releaseBall();
// λ½κΈ° ν맀 ν μ¬κ³ νμΈν΄μ μνλ₯Ό λ°κΏμ€λ€.
if (gachaMachine.getCount() > 0) {
// λ½κΈ° μ¬κ³ κ° μμΌλ©΄, ν맀λκΈ° μν
gachaMachine.setState(gachaMachine.getNoCoinState());
} else {
// λ½κΈ° μ¬κ³ κ° μμΌλ©΄, λ§€μ§ μν
System.out.println("λ½κΈ°κ° 맀μ§λμμ΅λλ€.");
gachaMachine.setState(gachaMachine.getSoldOutState());
}
}
@Override
public void refill() {
}
@Override
public String toString() {
return "νλ§€μ€ μνμ
λλ€ : λ½κΈ° ν맀μ€!";
}
}
3. λ½κΈ° κΈ°κ³ λ§λ€κΈ°
public class GachaMachine {
State soldOutState; // λ§€μ§ μν
State noCoinState; // ν맀λκΈ° μν : μ½μΈ μλ μν
State hasCoinState; // ν맀λκΈ° μν : μ½μΈ λ£μ΄μ§ μν
State soldState; // ν맀μ€μ μν
State state; // μν μΈμ€ν΄μ€
int count = 0; // λ½κΈ° κ°μ
public GachaMachine(int numberGumballs) {
soldOutState = new SoldOutState(this);
noCoinState = new NoCoinState(this);
hasCoinState = new HasCoinState(this);
soldState = new SoldState(this);
this.count = numberGumballs;
if (numberGumballs > 0) {
state = noCoinState;
} else {
state = soldOutState;
}
}
public void insertCoin() {
// λμ λ£μ
state.insertCoin();
}
public void ejectCoin() {
// λμ λ°νμ νλ©΄ λ°ν
state.ejectCoin();
}
public void turnCrank() {
// μμ‘μ΄ λλ¦Ό
state.turnCrank();
// λ½κΈ° ν맀μ¬λΆ
state.dispense();
}
public void releaseBall() {
System.out.println("λ½κΈ°κ° λμμ΅λλ€.");
if (count > 0) {
count = count - 1;
}
}
public void refill(int count) {
this.count = this.count + count;
System.out.println("λ½κΈ° " + count + " κ° λ¦¬ν!, νμ¬ " + this.count + " κ° μ
λλ€.");
state.refill();
}
public void setState(State state) {
this.state = state;
}
public int getCount() {
return count;
}
public State getState() {
return state;
}
public State getSoldOutState() {
return soldOutState;
}
public State getNoCoinState() {
return noCoinState;
}
public State getHasCoinState() {
return hasCoinState;
}
public State getSoldState() {
return soldState;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("===== Gumball Machine =====\n");
sb.append("λ¨Έμ μν : " + state + "\n");
sb.append("μ¬κ³ : " + count + " κ°\n");
return sb.toString();
}
}
μ 리νλ©΄
- κ° State μ νλ (Behavior) μ λ³κ°μ ν΄λμ€λ‘ κ΅μ§ν μμΌ°λ€.
- μ§μ λΆν ifλ¬Έλ€μ μμ νλ€.
- κ° State λ₯Ό λ³κ²½νλ κ²μ λν΄μλ λ«νμμΌλ©΄μ GachaMachine μ체λ μλ‘μ΄ μν ν΄λμ€λ₯Ό μΆκ°νλ νμ₯μ λν΄μλ μ΄λ €μλ€. (OCP μμΉ)
- μ²μμ μꡬλ°μ μν λ€μ΄μ΄κ·Έλ¨μ ν¨μ¬ κ°κΉμ°λ©΄μλ μ΄ν΄νκΈ° μ’μ μ½λ λ² μ΄μ€μ ν΄λμ€ κ΅¬μ‘°λ₯Ό λ§λ€μλ€.
β μ€ν μ΄νΈ ν¨ν΄μ μ μ© ν¨κ³Ό
- λ΄λΆ μνκ° λ°λμ λ°λΌμ κ°μ²΄μ νλμ λ°κΏ μ μλ€.
μνλ₯Ό λ³λμ ν΄λμ€λ‘ μΊ‘μνν λ€μ νμ¬ μνμ μλ κ°μ²΄μκ² νλμ μμνκΈ° λλ¬Έμ λ΄λΆ μνκ° λ°λμ λ°λΌμ νλμ΄ λ¬λΌμ§κ² λλ κ²μ΄λ€. - ν΄λμ€κ° λ°λλ κ² κ°μ κ²°κ³Όλ₯Ό μ»λλ€.
ν΄λΌμ΄μΈνΈ μ μ₯μμ ν΄λΌμ΄μΈνΈμμ μ¬μ©νλ κ°μ²΄μ νλμ΄ μμ ν λ¬λΌμ§ μ μλ€λ©΄ λ§μΉ κ·Έ κ°μ²΄κ° λ€λ₯Έ ν΄λμ€λ‘λΆν° λ§λ€μ΄μ§ κ°μ²΄μ²λΌ λκ»΄μ§ μ μλ€. μ€μ λ‘λ λ€λ₯Έ ν΄λμ€λ‘ λ§λ€μ΄μ Έ λ³μ νλ κ²μ΄ μλλΌ κ΅¬μ±μ ν΅ν΄ μ¬λ¬ μν κ°μ²΄λ₯Ό λ°κΏκ°λ©΄μ μ¬μ©νκΈ° λλ¬Έμ λ°λλ κ²κ³Ό κ°μ κ²°κ³Όλ₯Ό μ»μ μ μλ κ²μ΄λ€.
β UML Diagram
Strategy Pattern (μ λ΅ ν¨ν΄ , μ€νΈλν°μ§ ν¨ν΄) κ³Ό UML Diagram μ΄ λμΌνλ€.
β Strategy Pattern κ³Ό μ°¨μ΄
μ©λμ μμ΄μ μ°¨μ΄μ μ΄ μλ€.
Strategy ν¨ν΄ | State ν¨ν΄ |
μΌλ°μ μΌλ‘ Client μμ Context κ°μ²΄νν
μ΄λ€ μ λ΅μ μ¬μ©ν μ§ μ§μ ν΄μ€λ€. μ£Όλ‘ μ€νμμ μ λ΅ κ°μ²΄ (Strategy Object) λ₯Ό λ³κ²½ν μ μλ μ μ°μ±μ μ 곡νκΈ° μν μ©λλ‘ μ°μΈλ€. |
State κ°μ²΄μ νλμ΄ μΊ‘μν λλ€. μν©μ λ°λΌ Context κ°μ²΄μμ μ¬λ¬ State κ°μ²΄ μ€ νλμ κ°μ²΄μκ² λͺ¨λ νλμ λ§‘κΈ°κ² λλ€. μ¦, Client λ State κ°μ²΄μ λν΄ μμΈν λͺ°λΌλ λλ€. |
SubClass λ₯Ό λ§λλ λ°©λ²μ λμ νμ¬ μ μ°μ±μ κ·Ήλν νκΈ° μν μ©λλ‘ μ¬μ©νλ€. μμμ μ΄μ©ν΄μ ν΄λμ€μ νλμ μ μνλ€λ³΄λ©΄ νλμ μ½κ² λ³κ²½νκΈ° μ΄λ ΅μ§λ§, Strategy ν¨ν΄μ μ¬μ©νλ©΄ ꡬμ±μ ν΅ν΄ νλμ μ μνλ κ°μ²΄λ₯Ό μ μ°νκ² λ°κΏ μ μλ€. |
Context κ°μ²΄μ μ§μ λΆνκ² μ‘°κ±΄λ¬Έμ λ£λ λμ μ¬μ©ν μ μλ ν¨ν΄μ΄λΌ ν μ μλ€. |
β μ 리
- State ν¨ν΄μ μ΄μ©νλ©΄ λ΄λΆ μνλ₯Ό λ°νμΌλ‘ μ¬λ¬ λ€λ₯Έ νλμ μ¬μ©ν μ μλ€.
- 쑰건문 λ±μ μ΄μ©νμ¬ μ§μ λΆνκ² μνλ₯Ό λνλ΄μ§ μκ³ κ° μνλ₯Ό ν΄λμ€λ₯Ό μ΄μ©νμ¬ νννκ²λλ€.
- κ° μνλ₯Ό ν΄λμ€λ‘ μΊ‘μν ν¨μΌλ‘μ¨ λμ€μ λ³κ²½μμΌμΌ ν λ΄μ©μ κ΅μ§ν ν μ μλ€.
- μΌλ°μ μΌλ‘ νλ λλ μκ³ λ¦¬μ¦μ Context ν΄λμ€λ₯Ό λ§λ€ λ μ€μ νλ€.
- μν μ νμ State ν΄λμ€μ μν΄μ μ μ΄ν μλ μκ³ , Context ν΄λμ€μ μν΄μ μ μ΄ν μλ μλ€.
- μνλ₯Ό ν΄λμ€λ‘ μμ±νκΈ°λλ¬Έμ ν΄λμ€μ κ°μκ° λμ΄λ μ μμ§λ§ μν©μ λ°λΌ ν΄λμ€μ μκ° λ λ§μνΈμ΄ λμ μ μλ€.
- State ν΄λμ€λ₯Ό μ¬λ¬ Context κ°μ²΄μ μΈμ€ν΄μ€μμ 곡μ νλλ‘ ν μλ μλ€.
μ°Έκ³
ν€λ νΌμ€νΈ λμμΈ ν¨ν΄ (Head First Design Patterns) - μλ¦ ν리먼,μ리μλ² μ€ λ‘μ¨,μΌμ΄μ μμλΌ,λ²νΈ λ² μ΄μΈ