๐ ์ํ(State) ํจํด - ์๋ฒฝ ๋ง์คํฐํ๊ธฐ
State Pattern
์ํ ํจํด(State Pattern)์ ๊ฐ์ฒด๊ฐ ํน์ ์ํ์ ๋ฐ๋ผ ํ์๋ฅผ ๋ฌ๋ฆฌํ๋ ์ํฉ์์, ์ํ๋ฅผ ์กฐ๊ฑด๋ฌธ์ผ๋ก ๊ฒ์ฌํด์ ํ์๋ฅผ ๋ฌ๋ฆฌํ๋ ๊ฒ์ด ์๋, ์ํ๋ฅผ ๊ฐ์ฒดํ ํ์ฌ ์ํ๊ฐ ํ๋์ ํ ์ ์๋๋ก ์์ํ๋ ํจํด์ ๋งํ๋ค.
๊ฐ์ฒด ์งํฅ ํ๋ก๊ทธ๋๋ฐ์์์ ํด๋์ค๋ ๊ผญ ์ฌ๋ฌผ / ์๋ฌผ๋ง์ ํํํ๋ ๊ณ ์ฒด ํํ์ ๋ฐ์ดํฐ๋ง ํํ ํ ์ ์๋๊ฒ ์๋๋ค. ๊ฒฝ์ฐ์ ๋ฐ๋ผ์ ๋ฌดํํ์ ํ์ / ๋์๋ ํด๋์ค๋ก ๋ฌถ์ด ํํํ ์ ์๋ค.
๊ทธ๋์ ์ํ๋ฅผ ํด๋์ค๋ก ํํํ๋ฉด ํด๋์ค๋ฅผ ๊ต์ฒดํด์ ‘์ํ์ ๋ณํ’๋ฅผ ํํํ ์ ์๊ณ , ๊ฐ์ฒด ๋ด๋ถ ์ํ ๋ณ๊ฒฝ์ ๋ฐ๋ผ ๊ฐ์ฒด์ ํ๋์ ์ํ์ ํนํ๋ ํ๋๋ค๋ก ๋ถ๋ฆฌํด ๋ผ ์ ์์ผ๋ฉฐ, ์๋ก์ด ํ๋์ ์ถ๊ฐํ๋๋ผ๋ ๋ค๋ฅธ ํ๋์ ์ํฅ์ ์ฃผ์ง ์๋๋ค.
์ฌ๊ธฐ์ '์ํ' ๋, ๊ฐ์ฒด๊ฐ ๊ฐ์ง ์ ์๋ ์ด๋ค ์กฐ๊ฑด์ด๋ ์ํฉ์ ์๋ฏธํ๋ค.
์๋ฅผ๋ค์ด ํฐ๋น๊ฐ ์ผ์ ธ ์๋ ์ํ๋ผ๋ฉด ์๋ ๋ฒํผ์ ๋๋ฅด๋ฉด ์๋์ด ์ฆ๊ฐํ๊ฑฐ๋ ๊ฐ์ํ๋ค. ํ์ง๋ง ํฐ๋น๊ฐ ๊บผ์ ธ ์๋ ์ํ๋ผ๋ฉด ์๋๋ฒํผ์ ์๋ฌด๋ฆฌ ๋๋ฌ๋ ํฐ๋น์ ์๋์ ๋ฐ๋์ง ์๋๋ค. ์ฆ, ํฐ๋น ์ ์์ ์ํ์ ๋ฐ๋ผ ๋ฉ์๋ ํ๋์ด ๋ฐ๋๋ ๊ฒ์ด๋ค.
์ด์ฒ๋ผ ๊ฐ์ฒด๊ฐ ํน์ ์ํ์ ๋ฐ๋ผ ํ์๋ฅผ ๋ฌ๋ฆฌํ๋ ์ํฉ์์ ์ฌ์ฉ๋๋ ์ต์ ์ ํจํด์ด state pattern ์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
์ ๋ต ํจํด(Strategy Pattern)์ด '์ ๋ต ์๊ณ ๋ฆฌ์ฆ'์ ํด๋์ค๋ก ํํํ ํจํด์ด๋ผ๋ฉด, ์ํ ํจํด(State Pattern)์ '๊ฐ์ฒด ์ํ'๋ฅผ ํด๋์ค๋ก ํํํ ํจํด์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค.
๊ทธ๋์ ๊ทธ๋ฐ์ง ์ํ ํจํด์ ํด๋์ค ๋ค์ด์ด๊ทธ๋จ์ ๋ณด๋ฉด ์ ๋ต ํจํด๊ณผ ๋งค์ฐ ์ ์ฌํ๋ค๋ ์ ์ ๋ณผ ์ ์๋ค. ์๋ํ๋ฉด ์ ๋ต ํจํด์ ์ ๋ต์ ๊ฐ์ฒดํ ํ๊ฑฐ๊ณ , ์ํ ํจํด์ ์ํ๋ฅผ ๊ฐ์ฒดํ ํ๊ฒ์ธ๋ฐ ์ด์จ๋ ๋๋ค ๋๊ฐ์ ํด๋์ค ๋ฌถ์์ด๊ธฐ ๋๋ฌธ์ด๋ค.
์ํ ํจํด ๊ตฌ์กฐ
- State ์ธํฐํ์ด์ค : ์ํ๋ฅผ ์ถ์ํํ ๊ณ ์์ค ๋ชจ๋.
- ConcreteState : ๊ตฌ์ฒด์ ์ธ ๊ฐ๊ฐ์ ์ํ๋ฅผ ํด๋์ค๋ก ํํ. State ์ญํ ๋ก ๊ฒฐ์ ๋๋ ์ธํฐํ์ด์ค(API)๋ฅผ ๊ตฌ์ฒด์ ์ผ๋ก ๊ตฌํํ๋ค. ๋ค์ ์ํ๊ฐ ๊ฒฐ์ ๋๋ฉด Context์ ์ํ ๋ณ๊ฒฝ์ ์์ฒญํ๋ ์ญํ ๋ ํ๋ค.
- Context : State๋ฅผ ์ด์ฉํ๋ ์์คํ . ์์คํ ์ํ๋ฅผ ๋ํ๋ด๋ State ๊ฐ์ฒด๋ฅผ ํฉ์ฑ(composition)ํ์ฌ ๊ฐ์ง๊ณ ์๋ค. ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์์ฒญ๋ฐ์ผ๋ฉด State ๊ฐ์ฒด์ ํ์ ์คํ์ ์์ํ๋ค.
์ํ ํด๋์ค๋ ์ฑ๊ธํค ํด๋์ค๋ก ๊ตฌ์ฑํ๋ค.
์ ๋ต ํจํด์ ์ ๋ต ๊ฐ์ฒด ๊ฐ์ ๊ฒฝ์ฐ ๋งค๊ฐ ๊ฐ์ ๋ฐ๋ผ ์๊ณ ๋ฆฌ์ฆ ์ํ ํํ๊ฐ ๋ฌ๋ผ์ง์ ์์ง๋ง, ์ํ๋ ๊ทธ ๊ฐ์ฒด์ ํ ํผ์ ๋ํ๋ด๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋๋ถ๋ถ์ ์ํฉ์์ ์ ์ผํ๊ฒ ์์ด์ผ ํ๋ค.
์ํ ํจํด ํ๋ฆ
ํด๋์ค ๊ตฌ์ฑ
interface AbstractState {
void requestHandle(Context cxt);
}
class ConcreteStateA implements AbstractState {
@Override
public void requestHandle(Context cxt) {}
}
class ConcreteStateB implements AbstractState {
@Override
public void requestHandle(Context cxt) {
// ์ํ์์ ๋์์ ์คํํ ํ ๋ฐ๋ก ๋ค๋ฅธ ์ํ๋ก ๋ฐ๊พธ๊ธฐ๋ ํจ
// ์๋ฅผ ๋ค์ด ์ ์ on ์ํ์์ ๋๊ธฐ ๋์์ ์คํํํ ๊ฐ์ฒด ์ํ๋ฅผ ์ ์ off๋ก ๋ณ๊ฒฝ ํ๋ฏ์ด
cxt.setState(ConcreteStateC.getInstance());
}
}
class ConcreteStateC implements AbstractState {
@Override
public void requestHandle(Context cxt) {}
}
class Context {
AbstractState state; // composition
void setState(AbstractState state) {
this.state = state;
}
// ์ํ์ ์์กดํ ์ฒ๋ฆฌ ๋ฉ์๋๋ก์ state ๊ฐ์ฒด์ ์ฒ๋ฆฌ๋ฅผ ์์ํจ
void request() {
state.requestHandle(this);
}
}
ํด๋์ค ํ๋ฆ
class Client {
public static void main(String[] args) {
Context context = new Context();
// 1. StateA ์ํ ์ค์
context.setState(new ConcreteStateA());
// 2. ํ์ฌ StateA ์ํ์ ๋ง๋ ๋ฉ์๋ ์คํ
context.request();
// 3. StateB ์ํ ์ค์
context.setState(new ConcreteStateB());
// 4. StateB ์ํ์์ ๋๋ค๋ฅธ StateC ์ํ๋ก ๋ณ๊ฒฝ
context.request();
// 5. StateC ์ํ์ ๋ง๋ ๋ฉ์๋ ์คํ
context.request();
}
}
์ํ ํจํด ํน์ง
์ํ ํจํด ์ฌ์ฉ ์๊ธฐ
- ๊ฐ์ฒด์ ํ๋(๋ฉ์๋)๊ฐ ์ํ(state)์ ๋ฐ๋ผ ๊ฐ๊ธฐ ๋ค๋ฅธ ๋์์ ํ ๋.
- ์ํ ๋ฐ ์ ํ์ ๊ฑธ์ณ ๋๊ท๋ชจ ์กฐ๊ฑด ๋ถ๊ธฐ ์ฝ๋์ ์ค๋ณต ์ฝ๋๊ฐ ๋ง์ ๊ฒฝ์ฐ
- ์กฐ๊ฑด๋ฌธ์ ๊ฐ ๋ถ๊ธฐ๋ฅผ ๋ณ๋์ ํด๋์ค์ ๋ฃ๋๊ฒ์ด ์ํ ํจํด์ ํต์ฌ
- ๋ฐํ์๋จ์์ ๊ฐ์ฒด์ ์ํ๋ฅผ ์ ๋์ ์ผ๋ก ๋ณ๊ฒฝํด์ผ ํ ๋
์ํ ํจํด ์ฅ์
- ์ํ(State)์ ๋ฐ๋ฅธ ๋์์ ๊ฐ๋ณ ํด๋์ค๋ก ์ฎ๊ฒจ์ ๊ด๋ฆฌ ํ ์ ์๋ค.
- ์ํ(State)์ ๊ด๋ จ๋ ๋ชจ๋ ๋์์ ๊ฐ๊ฐ์ ์ํ ํด๋์ค์ ๋ถ์ฐ์ํด์ผ๋ก์จ, ์ฝ๋ ๋ณต์ก๋๋ฅผ ์ค์ผ ์ ์๋ค.
- ๋จ์ผ ์ฑ ์ ์์น์ ์ค์ํ ์ ์๋ค. (ํน์ ์ํ์ ๊ด๋ จ๋ ์ฝ๋๋ฅผ ๋ณ๋์ ํด๋์ค๋ก ๊ตฌ์ฑ)
- ๊ฐ๋ฐฉ ํ์ ์์น์ ์ค์ ํ ์ ์๋ค. (๊ธฐ์กด State ํด๋์ค๋ ์ปจํ ์คํธ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์ State๋ฅผ ๋์ ํ ์ ์๋ค)
- ํ๋์ ์ํ ๊ฐ์ฒด๋ง ์ฌ์ฉํ์ฌ ์ํ ๋ณ๊ฒฝ์ ํ๋ฏ๋ก ์ผ๊ด์ฑ ์๋ ์ํ ์ฃผ์ ์ ๋ฐฉ์งํ๋๋ฐ ๋์์ด ๋๋ค.
์ํ ํจํด ๋จ์
- ์ํ ๋ณ๋ก ํด๋์ค๋ฅผ ์์ฑํ๋ฏ๋ก, ๊ด๋ฆฌํด์ผํ ํด๋์ค ์ ์ฆ๊ฐ
- ์ํ ํด๋์ค ๊ฐฏ์๊ฐ ๋ง๊ณ ์ํ ๊ท์น์ด ์์ฃผ ๋ณ๊ฒฝ๋๋ค๋ฉด, Context์ ์ํ ๋ณ๊ฒฝ ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ฒ ๋ ์ ์๋ค.
- ๊ฐ์ฒด์ ์ ์ฉํ ์ํ๊ฐ ๋ช๊ฐ์ง ๋ฐ์ ์๊ฑฐ๋ ๊ฑฐ์ ์ํ ๋ณ๊ฒฝ์ด ์ด๋ฃจ์ด์ง์ง ์๋ ๊ฒฝ์ฐ ํจํด์ ์ ์ฉํ๋ ๊ฒ์ด ๊ณผ๋ํ ์ ์๋ค.
์์ ๋ฅผ ํตํด ์์๋ณด๋ State ํจํด
๋ ธํธ๋ถ ์ ์ ์ํ์ ๋ฐ๋ฅธ ๋์ ์ค๊ณ
๋ ธํธ๋ถ์ ์ผ๊ณ ๋๋ ์ํฉ์ ์๊ฐํด ๋ณด์.
๋ ธํธ๋ถ์์ ์ ์ ๋ฒํผ์ ๋๋ฅด๊ฒ ๋๋ฉด ๋ํ๋๋ ์ํ ๋ณํ๋ ๋ค์๊ณผ ๊ฐ์ด 3๋จ๊ณ๋ก ์ด๋ฃจ์ด ์ง๋ค.
- ๋ ธํธ๋ถ ์ ์ ON ์ํ์์ ์ ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ ธํธ๋ถ์ด ์ ์ OFF ์ํ๋ก ๋ณ๊ฒฝ
- ๋ ธํธ๋ถ ์ ์ OFF ์ํ์์ ์ ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ ธํธ๋ถ์ด ์ ์ ON ์ํ๋ก ๋ณ๊ฒฝ
- ๋ ธํธ๋ถ ์ ์ ์ ์ ๋ชจ๋ ์ํ์์ ์ ์ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ ธํธ๋ถ์ด ์ ์ ON ์ํ๋ก ๋ณ๊ฒฝ
ํด๋ฆฐํ์ง ์์ ๋ฌธ์ ์ ์ฝ๋ โ
๋ณดํต์ด๋ผ๋ฉด, ์ํ์ ๋ฐ๋ฅธ ๋์ ๋ถ๊ธฐ๋ if๋ฌธ์ด๋ switch ๋ฌธ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ๋ง๋ จ์ด๋ค.
class Laptop {
// ์ํ๋ฅผ ๋ํ๋ด๋ ์์
public static final int OFF = 0;
public static final int SAVING = 1;
public static final int ON = 2;
// ์ํ๋ฅผ ์ ์ฅํ๋ ๋ณ์
private int powerState;
Laptop() {
this.powerState = Laptop.OFF; // ์ด๊ธฐ๋ ๋
ธํธ๋ถ์ด ๊บผ์ง ์ํ
}
// ์ํ ๋ณ๊ฒฝ
void changeState(int state) {
this.powerState = state;
}
// ์ ์ ๋ฒํผ ํด๋ฆญ
void powerButtonPush() {
if (powerState == Laptop.OFF) {
System.out.println("์ ์ on");
changeState(Laptop.ON);
} else if (powerState == Laptop.ON) {
System.out.println("์ ์ off");
changeState(Laptop.OFF);
} else if (powerState == Laptop.SAVING) {
System.out.println("์ ์ on");
changeState(Laptop.ON);
}
}
void setSavingState() {
System.out.println("์ ์ ๋ชจ๋");
changeState(Laptop.SAVING);
}
void typebuttonPush() {
if (powerState == Laptop.OFF) {
throw new IllegalStateException("๋
ธํธ๋ถ์ด OFF ์ธ ์ํ");
} else if (powerState == Laptop.ON) {
System.out.println("ํค ์
๋ ฅ");
} else if (powerState == Laptop.SAVING) {
throw new IllegalStateException("๋
ธํธ๋ถ์ด ์ ์ ๋ชจ๋์ธ ์ํ");
}
}
void currentStatePrint() {
if (powerState == Laptop.OFF) {
System.out.println("๋
ธํธ๋ถ์ด ์ ์ ON ์ธ ์ํ ์
๋๋ค.");
} else if (powerState == Laptop.ON) {
System.out.println("๋
ธํธ๋ถ์ด ์ ์ ON ์ธ ์ํ ์
๋๋ค.");
} else if (powerState == Laptop.SAVING) {
System.out.println("๋
ธํธ๋ถ์ด ์ ์ ๋ชจ๋ ์ธ ์ํ ์
๋๋ค.");
}
}
}
class Client {
public static void main(String args[]) {
LaptopContext laptop = new LaptopContext();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ์ผ๊ธฐ : OffState -> OnState
laptop.powerButtonPush();
laptop.currentStatePrint();
laptop.typebuttonPush();
// ๋
ธํธ๋ถ ์ ์ ํ๊ธฐ : OnState -> SavingState
laptop.setSavingState();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ๋ค์ ์ผ๊ธฐ : SavingState -> OnState
laptop.powerButtonPush();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ๋๊ธฐ : OnState -> OffState
laptop.powerButtonPush();
laptop.currentStatePrint();
}
}
๊ทธ๋ฌ๋ ์ํ ๋ณ์๋ ๊ฐ๋จํ ์๋ฃจ์ ์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค๋ฌด์์ ์ด๋ค ๊ฒฝ์ฐ์๋ ์ข์ง ์์ ๋ฐฉ๋ฒ์ด๋ค.
์ํ ๋ณ์๋ ๋ณ์์ ํ์์์ ๊ฒฐํฉ์ ๋ง๋ค์ด ๋ด๊ณ , ์ด ๊ณผ์ ์์ ์กฐ๊ฑด๋ฌธ๋ค์ ๋ถ์์ ์ผ๋ก ์์ฐํด ๋ด๊ธฐ ๋๋ฌธ์ด๋ค. ๋ฐ๋ผ์ ์ธ์ด์ ์ผ๋ก ํ์ฉ๋๋ ํ ์ํ ๋ณ์๋ ์ต๋ํ ์์ ์ฃผ๋ ๊ฒ์ด ์ข๋ค. (enum์ ์ฌ์ฉํด๋ ๋ง์ฐฌ๊ฐ์ง์ด๋ค. ํต์ฌ์ ์ํ์ ์์ํ๋ฅผ ์์ ํ๋ผ ์ด๋ค)
์์ ์ฝ๋์ ๋ํ ๋จ์ ์ ๋์ดํ๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
- ๊ฐ์ฒด ์งํฅ์ ์ฝ๋๊ฐ ์๋๋ค. (ํ๋์ฝ๋ฉ ์คํ์ผ)
- ์ํ ์ ํ์ด ๋ณต์กํ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฌธ์ ๋์ด๋์ด ์์ด ๊ฐ๋ ์ฑ์ด ์ข์ง ์๋ค.
- ๋ฐ๋๋ ๋ถ๋ถ๋ค์ด ์บก์ํ ๋์ด์์ง ์์ ๋ ธ์ถ๋์ด์๋ค.
- ๋ง์ผ ์ํ ๊ธฐ๋ฅ์ ์๋ก ์ถ๊ฐํ ๊ฒฝ์ฐ ๋ฉ์๋๋ฅผ ํต์ง๋ก ์์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์, OCP ์์น์ ์๋ฐฐ ๋๋ค.
์ํ ํจํด์ ์ ์ฉํ ์ฝ๋ โ๏ธ
์ํ ํจํด์ ํต์ฌ์ '์ํ'๋ฅผ ๊ฐ์ฒดํ ํ๋ผ๋ ๊ฒ์ด๋ค. (๊ฐ์ฒด๋ฅผ ์งํฅํ๋ผ)
๋ ธํธ๋ถ์ ์ํ 3๊ฐ์ง๋ฅผ ๋ชจ๋ ํด๋์ค๋ก ๊ตฌ์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ธํฐํ์ด์ค๋ ์ถ์ํด๋์ค๋ก ๋ฌถ์ด ์ถ์ํ / ์บก์ํ(์ ๋ณด ์๋)๋ฅผ ํ๋ค. ์ํ๋ฅผ ํด๋์ค๋ก ๋ถ๋ฆฌํ์์ผ๋, ์ํ์ ๋ฐ๋ฅธ ํ๋ ๋ฉ์๋๋ ๊ฐ ์ํ ํด๋์ค๋ง๋ค ๊ตฌํ์ ํด์ค๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก ์ฝ๋์ ์ ์ฒด ๋ผ์ธ์๊ฐ ๊ธธ์ด์ง๊ณ ๊ดํ ํด๋์ค๋ ๋์ง๋์ง ๋ง์์ ธ์ ์ฝ๊ธฐ ๊ฑฐ๋ถํด ๋ณด์ผ ๊ฒ ๊ฐ์ง๋ง, ์คํ๋ ค ์ด๋ฌํ ๋ฐฉ๋ฒ์ด ๋์ค์ ์ ์ง๋ณด์๋ฅผ ์ฉ์ดํ๊ฒ ํด์ค๋ค.
interface PowerState {
void powerButtonPush(LaptopContext cxt);
void typebuttonPush();
}
class OnState implements PowerState {
@Override
public void powerButtonPush(LaptopContext cxt) {
System.out.println("๋
ธํธ๋ถ ์ ์ OFF");
cxt.changeState(new OffState());
}
@Override
public void typebuttonPush() {
System.out.println("ํค ์
๋ ฅ");
}
@Override
public String toString() {
return "๋
ธํธ๋ถ์ด ์ ์ ON ์ธ ์ํ ์
๋๋ค.";
}
}
class OffState implements PowerState {
@Override
public void powerButtonPush(LaptopContext cxt) {
System.out.println("๋
ธํธ๋ถ ์ ์ ON");
cxt.changeState(new OnState());
}
@Override
public void typebuttonPush() {
throw new IllegalStateException("๋
ธํธ๋ถ์ด OFF ์ธ ์ํ");
}
@Override
public String toString() {
return "๋
ธํธ๋ถ์ด ์ ์ OFF ์ธ ์ํ ์
๋๋ค.";
}
}
class SavingState implements PowerState {
@Override
public void powerButtonPush(LaptopContext cxt) {
System.out.println("๋
ธํธ๋ถ ์ ์ on");
cxt.changeState(new OnState());
}
@Override
public void typebuttonPush() {
throw new IllegalStateException("๋
ธํธ๋ถ์ด ์ ์ ๋ชจ๋์ธ ์ํ");
}
@Override
public String toString() {
return "๋
ธํธ๋ถ์ด ์ ์ ๋ชจ๋ ์ธ ์ํ ์
๋๋ค.";
}
}
class LaptopContext {
PowerState powerState;
LaptopContext() {
this.powerState = new OffState();
}
void changeState(PowerState state) {
this.powerState = state;
}
void setSavingState() {
System.out.println("๋
ธํธ๋ถ ์ ์ ๋ชจ๋");
changeState(new SavingState());
}
void powerButtonPush() {
powerState.powerButtonPush(this);
}
void typebuttonPush() {
powerState.typebuttonPush();
}
void currentStatePrint() {
System.out.println(powerState.toString());
}
}
class Client {
public static void main(String[] args) {
LaptopContext laptop = new LaptopContext();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ์ผ๊ธฐ : OffState -> OnState
laptop.powerButtonPush();
laptop.currentStatePrint();
laptop.typebuttonPush();
// ๋
ธํธ๋ถ ์ ์ ํ๊ธฐ : OnState -> SavingState
laptop.setSavingState();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ๋ค์ ์ผ๊ธฐ : SavingState -> OnState
laptop.powerButtonPush();
laptop.currentStatePrint();
// ๋
ธํธ๋ถ ๋๊ธฐ : OnState -> OffState
laptop.powerButtonPush();
laptop.currentStatePrint();
}
}
๋ฆฌํฉํ ๋ง ํ๊ธฐ (์ฑ๊ธํค ์ ์ฉ) ๐จ๐ง
ํ์ง๋ง ์์ง ์์ ์ฝ๋์๋ ๋ฌธ์ ๊ฐ ์๋ค. ์ํ๋ฅผ ๋ณ๊ฒฝํ ๋ ๋ง๋ค ์๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ค๋ ์ ์ด๋ค.
๋ฌผ๋ก ์ฐ๊ฒฐ์ด ๋๊ธด ์ํ ๊ฐ์ฒด๋ JVM์ ๊ฐ๋น์ง ์ปฌ๋ ์ (GC) ์ ์ํด ์๋์ผ๋ก ์ง์์ง๊ฒ ์ง๋ง, ์ด๋ฐ ๊ฐ๋น์ง ๊ฐ์ด ๋์ด๋๊ฒ๋๋ฉด ๋์ค์ ๊ฐ์ฒด ์ ๊ฑฐ ๊ณผ์ ์์ Stop-the-world ๊ฐ ์ผ์ด๋๊ฒ ๋๋ค. (ํ๋ก๊ทธ๋จ์ด ๋ ๊ฑธ๋ฆผ)
์ ๋งํ ์ํฉ์์ ์ํ๋ ์๋ก ์ธ์คํด์คํ ํ ํ์๊ฐ ์ ํ ์๋ค. ๊ดํ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น์ธ ์ ์ด๋ค. ๋ฐ๋ผ์ ๊ฐ ์ํ ํด๋์ค๋ค์ ์ฑ๊ธํค(Singleton) ํ ์ํจ๋ค.
class OnState implements PowerState {
// Thread-Safe ํ ์ฑ๊ธํค ๊ฐ์ฒดํ
private OnState() {}
private static class SingleInstanceHolder {
private static final OnState INSTANCE = new OnState();
}
public static OnState getInstance() {
return SingleInstanceHolder.INSTANCE;
}
@Override
public void powerButtonPush(LaptopContext cxt) {
System.out.println("๋
ธํธ๋ถ ์ ์ OFF");
cxt.changeState(OffState.getInstance()); // ์ฑ๊ธํค ๊ฐ์ฒด ์ป๊ธฐ
}
@Override
public void typebuttonPush() {
System.out.println("ํค ์
๋ ฅ");
}
@Override
public String toString() {
return "๋
ธํธ๋ถ์ด ์ ์ ON ์ธ ์ํ ์
๋๋ค.";
}
}
// ...
๋น์ทํ ๋์์ธ ํจํด ๋น๊ต
State vs Strategy
ํจํด ์ ์ฌ์
- ์ ๋ต ํจํด๊ณผ ์ํ ํจํด์ ํด๋์ค ๋ค์ด์ด๊ทธ๋จ์ด ๊ฑฐ์ ๋์ผํ๊ณ ์ฝ๋ ์ฌ์ฉ๋ฒ๋ ๋น์ทํ๋ค.
- ๋๋ค ๋์กํ ์กฐ๊ฑด ๋ถ๊ธฐ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด ์ ๋ต / ์ํ ํํ๋ฅผ ๊ฐ์ฒดํ
- ๋๋ค ํฉ์ฑ(composition)์ ํตํด ์์์ ํ๊ณ๋ฅผ ๊ทน๋ณต
- ๋๋ค ๊ฐ์ฒด์ ์ผ๋ จ์ ํ๋์ด ์บก์ํ๋์ด ๊ฐ์ฒด ์งํฅ ์์น์ ์ค์ํ๋ค.
- State๋ Strategy์ ํ์ฅ์ผ๋ก ๊ฐ์ฃผ๋ ์๋ ์๋ค.
ํจํด ์ฐจ์ด์
- ์ ๋ต ํจํด๊ณผ ์ํ ํจํด์ ๊ตฌ์กฐ๋ ๊ฑฐ์ ๊ฐ์ง๋ง ์ด๋ค ๋ชฉ์ ์ ์ํด์ ์ฌ์ฉ๋๋๊ฐ์ ๋ฐ๋ผ ์ฐจ์ด๊ฐ ์๋ค.
- ์ ๋ต ํจํด์ ์๊ณ ๋ฆฌ์ฆ์ ๊ฐ์ฒดํ ํ์ฌ ํด๋ผ์ด์ธํธ์์ ์ ์ฐ์ ์ผ๋ก ์ ๋ต์ ์ ๊ณต / ๊ต์ฒด๋ฅผ ํ๋ค.
์ํ ํจํด์ ๊ฐ์ฒด์ ์ํ๋ฅผ ๊ฐ์ฒดํํ์ฌ ํด๋ผ์ด์ธํธ์ ์ํ ํด๋์ค ๋ด๋ถ์์ ๋ค๋ฅธ ์ํ๋ก ๊ต์ฒด๋ฅผ ํ๋ค. - ์ ๋ต ํจํด์ ์ ๋ต ๊ฐ์ฒด๋ ๊ทธ ์ ๋ต๋ง์ ์๊ณ ๋ฆฌ์ฆ ๋์์ ์ ์ ๋ฐ ์ํํ๋ค. (๋ง์ผ ์ ๋ต์ ์ํํ ํ๋ฉด ํด๋์ค ํญ๋ฐ์ด ์ผ์ด๋ ์ ์๋ค)
์ํ ํจํด์ ์ํ ๊ฐ์ฒด๋ ์ํ๊ฐ ์ ์ฉ๋๋ ๋์ ๊ฐ์ฒด๊ฐ ํ ์์๋ ์ผ๋ จ์ ๋ชจ๋ ํ๋๋ค์ ์ ์ ๋ฐ ์ํํ๋ค. - ์ ๋ต ํจํด์ ์ ๋ต ๊ฐ์ฒด๋ ์
๋ ฅ๊ฐ์ ๋ฐ๋ผ ์ ๋ต ํํ๊ฐ ๋ค์ํ๊ฒ ๋ ์ ์์ผ๋ ์ธ์คํด์ค๋ก ๊ตฌ์ฑํ๋ค.
์ํ ํจํด์ ์ํ ๊ฐ์ฒด๋ ์ ์๋ ์ํ๋ฅผ ์๋ก ์ค์์นญ ํ๊ธฐ์ ๋ฉ๋ชจ๋ฆฌ ์ ์ฝ์ ์ํด ์ฑ๊ธํค์ผ๋ก ๊ตฌ์ฑํ๋ค.
# ์ฐธ๊ณ ์๋ฃ
https://refactoring.guru/design-patterns/state
https://reactiveprogramming.io/blog/en/design-patterns/state