๋””์ž์ธ ํŒจํ„ด/GOF

๐Ÿ’  ์ƒํƒœ(State) ํŒจํ„ด - ์™„๋ฒฝ ๋งˆ์Šคํ„ฐํ•˜๊ธฐ

์ธํŒŒ_ 2022. 12. 5. 08:36

State-Pattern
State-Pattern

State Pattern

์ƒํƒœ ํŒจํ„ด(State Pattern)์€ ๊ฐ์ฒด๊ฐ€ ํŠน์ • ์ƒํƒœ์— ๋”ฐ๋ผ ํ–‰์œ„๋ฅผ ๋‹ฌ๋ฆฌํ•˜๋Š” ์ƒํ™ฉ์—์„œ, ์ƒํƒœ๋ฅผ ์กฐ๊ฑด๋ฌธ์œผ๋กœ ๊ฒ€์‚ฌํ•ด์„œ ํ–‰์œ„๋ฅผ ๋‹ฌ๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ์ƒํƒœ๋ฅผ ๊ฐ์ฒดํ™” ํ•˜์—ฌ ์ƒํƒœ๊ฐ€ ํ–‰๋™์„ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์œ„์ž„ํ•˜๋Š” ํŒจํ„ด์„ ๋งํ•œ๋‹ค.

๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ์˜ ํด๋ž˜์Šค๋Š” ๊ผญ ์‚ฌ๋ฌผ / ์ƒ๋ฌผ๋งŒ์„ ํ‘œํ˜„ํ•˜๋Š” ๊ณ ์ฒด ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋งŒ ํ‘œํ˜„ ํ•  ์ˆ˜ ์žˆ๋Š”๊ฒŒ ์•„๋‹ˆ๋‹ค. ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์„œ ๋ฌดํ˜•ํƒœ์˜ ํ–‰์œ„ / ๋™์ž‘๋„ ํด๋ž˜์Šค๋กœ ๋ฌถ์–ด ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ƒํƒœ๋ฅผ ํด๋ž˜์Šค๋กœ ํ‘œํ˜„ํ•˜๋ฉด ํด๋ž˜์Šค๋ฅผ ๊ต์ฒดํ•ด์„œ ‘์ƒํƒœ์˜ ๋ณ€ํ™”’๋ฅผ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ , ๊ฐ์ฒด ๋‚ด๋ถ€ ์ƒํƒœ ๋ณ€๊ฒฝ์— ๋”ฐ๋ผ ๊ฐ์ฒด์˜ ํ–‰๋™์„ ์ƒํƒœ์— ํŠนํ™”๋œ ํ–‰๋™๋“ค๋กœ ๋ถ„๋ฆฌํ•ด ๋‚ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ƒˆ๋กœ์šด ํ–‰๋™์„ ์ถ”๊ฐ€ํ•˜๋”๋ผ๋„ ๋‹ค๋ฅธ ํ–‰๋™์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋Š”๋‹ค.

์—ฌ๊ธฐ์„œ '์ƒํƒœ' ๋ž€, ๊ฐ์ฒด๊ฐ€ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋Š” ์–ด๋–ค ์กฐ๊ฑด์ด๋‚˜ ์ƒํ™ฉ์„ ์˜๋ฏธํ•œ๋‹ค.
์˜ˆ๋ฅผ๋“ค์–ด ํ‹ฐ๋น„๊ฐ€ ์ผœ์ ธ ์žˆ๋Š” ์ƒํƒœ๋ผ๋ฉด ์Œ๋Ÿ‰ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์Œ๋Ÿ‰์ด ์ฆ๊ฐ€ํ•˜๊ฑฐ๋‚˜ ๊ฐ์†Œํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ‹ฐ๋น„๊ฐ€ ๊บผ์ ธ ์žˆ๋Š” ์ƒํƒœ๋ผ๋ฉด ์Œ๋Ÿ‰๋ฒ„ํŠผ์„ ์•„๋ฌด๋ฆฌ ๋ˆŒ๋Ÿฌ๋„ ํ‹ฐ๋น„์˜ ์Œ๋Ÿ‰์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ํ‹ฐ๋น„ ์ „์›์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ๋ฉ”์†Œ๋“œ ํ–‰๋™์ด ๋ฐ”๋€Œ๋Š” ๊ฒƒ์ด๋‹ค.
์ด์ฒ˜๋Ÿผ ๊ฐ์ฒด๊ฐ€ ํŠน์ • ์ƒํƒœ์— ๋”ฐ๋ผ ํ–‰์œ„๋ฅผ ๋‹ฌ๋ฆฌํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ตœ์ ์˜ ํŒจํ„ด์ด state pattern ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

์ „๋žต ํŒจํ„ด(Strategy Pattern)์ด '์ „๋žต ์•Œ๊ณ ๋ฆฌ์ฆ˜'์„ ํด๋ž˜์Šค๋กœ ํ‘œํ˜„ํ•œ ํŒจํ„ด์ด๋ผ๋ฉด, ์ƒํƒœ ํŒจํ„ด(State Pattern)์€ '๊ฐ์ฒด ์ƒํƒœ'๋ฅผ ํด๋ž˜์Šค๋กœ ํ‘œํ˜„ํ•œ ํŒจํ„ด์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

๊ทธ๋ž˜์„œ ๊ทธ๋Ÿฐ์ง€ ์ƒํƒœ ํŒจํ„ด์˜ ํด๋ž˜์Šค ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋ณด๋ฉด ์ „๋žต ํŒจํ„ด๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•˜๋‹ค๋Š” ์ ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ „๋žต ํŒจํ„ด์€ ์ „๋žต์„ ๊ฐ์ฒดํ™” ํ•œ๊ฑฐ๊ณ , ์ƒํƒœ ํŒจํ„ด์€ ์ƒํƒœ๋ฅผ ๊ฐ์ฒดํ™” ํ•œ๊ฒƒ์ธ๋ฐ ์–ด์จ‹๋“  ๋‘˜๋‹ค ๋˜‘๊ฐ™์€ ํด๋ž˜์Šค ๋ฌถ์Œ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

 

์ƒํƒœ ํŒจํ„ด ๊ตฌ์กฐ

State-Pattern

  • State ์ธํ„ฐํŽ˜์ด์Šค : ์ƒํƒœ๋ฅผ ์ถ”์ƒํ™”ํ•œ ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ.
  • ConcreteState : ๊ตฌ์ฒด์ ์ธ ๊ฐ๊ฐ์˜ ์ƒํƒœ๋ฅผ ํด๋ž˜์Šค๋กœ ํ‘œํ˜„. State ์—ญํ• ๋กœ ๊ฒฐ์ •๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค(API)๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค. ๋‹ค์Œ ์ƒํƒœ๊ฐ€ ๊ฒฐ์ •๋˜๋ฉด Context์— ์ƒํƒœ ๋ณ€๊ฒฝ์„ ์š”์ฒญํ•˜๋Š” ์—ญํ• ๋„ ํ•œ๋‹ค.
  • Context : State๋ฅผ ์ด์šฉํ•˜๋Š” ์‹œ์Šคํ…œ. ์‹œ์Šคํ…œ ์ƒํƒœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” State ๊ฐ์ฒด๋ฅผ ํ•ฉ์„ฑ(composition)ํ•˜์—ฌ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ ์š”์ฒญ๋ฐ›์œผ๋ฉด State ๊ฐ์ฒด์— ํ–‰์œ„ ์‹คํ–‰์„ ์œ„์ž„ํ•œ๋‹ค.
์ƒํƒœ ํด๋ž˜์Šค๋Š” ์‹ฑ๊ธ€ํ†ค ํด๋ž˜์Šค๋กœ ๊ตฌ์„ฑํ•œ๋‹ค.
์ „๋žต ํŒจํ„ด์˜ ์ „๋žต ๊ฐ์ฒด ๊ฐ™์€ ๊ฒฝ์šฐ ๋งค๊ฐœ ๊ฐ’์— ๋”ฐ๋ผ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ˆ˜ํ–‰ ํ˜•ํƒœ๊ฐ€ ๋‹ฌ๋ผ์งˆ์ˆ˜ ์žˆ์ง€๋งŒ, ์ƒํƒœ๋Š” ๊ทธ ๊ฐ์ฒด์˜ ํ˜„ ํผ์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋Œ€๋ถ€๋ถ„์˜ ์ƒํ™ฉ์—์„œ ์œ ์ผํ•˜๊ฒŒ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์ƒํƒœ ํŒจํ„ด ํ๋ฆ„

 

ํด๋ž˜์Šค ๊ตฌ์„ฑ

State-Pattern

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);
    }
}

 

ํด๋ž˜์Šค ํ๋ฆ„

State-Pattern

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๋‹จ๊ณ„๋กœ ์ด๋ฃจ์–ด ์ง„๋‹ค.

  1. ๋…ธํŠธ๋ถ ์ „์› ON ์ƒํƒœ์—์„œ ์ „์› ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋…ธํŠธ๋ถ์ด ์ „์› OFF ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
  2. ๋…ธํŠธ๋ถ ์ „์› OFF ์ƒํƒœ์—์„œ ์ „์› ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋…ธํŠธ๋ถ์ด ์ „์› ON ์ƒํƒœ๋กœ ๋ณ€๊ฒฝ
  3. ๋…ธํŠธ๋ถ ์ „์› ์ ˆ์ „ ๋ชจ๋“œ ์ƒํƒœ์—์„œ ์ „์› ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ๋…ธํŠธ๋ถ์ด ์ „์› 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();
    }
}

State-Pattern

๊ทธ๋Ÿฌ๋‚˜ ์ƒํƒœ ๋ณ€์ˆ˜๋Š” ๊ฐ„๋‹จํ•œ ์†”๋ฃจ์…˜ ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ์‹ค๋ฌด์—์„  ์–ด๋–ค ๊ฒฝ์šฐ์—๋„ ์ข‹์ง€ ์•Š์€ ๋ฐฉ๋ฒ•์ด๋‹ค.

์ƒํƒœ ๋ณ€์ˆ˜๋Š” ๋ณ€์ˆ˜์™€ ํ–‰์œ„์™€์˜ ๊ฒฐํ•ฉ์„ ๋งŒ๋“ค์–ด ๋‚ด๊ณ , ์ด ๊ณผ์ •์—์„œ ์กฐ๊ฑด๋ฌธ๋“ค์„ ๋ถ€์ˆ˜์ ์œผ๋กœ ์ƒ์‚ฐํ•ด ๋‚ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์–ธ์–ด์ ์œผ๋กœ ํ—ˆ์šฉ๋˜๋Š” ํ•œ ์ƒํƒœ ๋ณ€์ˆ˜๋Š” ์ตœ๋Œ€ํ•œ ์—†์• ์ฃผ๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. (enum์„ ์‚ฌ์šฉํ•ด๋„ ๋งˆ์ฐฌ๊ฐ€์ง€์ด๋‹ค. ํ•ต์‹ฌ์€ ์ƒํƒœ์˜ ์ƒ์ˆ˜ํ™”๋ฅผ ์ž์ œํ•˜๋ผ ์ด๋‹ค)

์œ„์˜ ์ฝ”๋“œ์— ๋Œ€ํ•œ ๋‹จ์ ์„ ๋‚˜์—ดํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  1. ๊ฐ์ฒด ์ง€ํ–ฅ์  ์ฝ”๋“œ๊ฐ€ ์•„๋‹ˆ๋‹ค. (ํ•˜๋“œ์ฝ”๋”ฉ ์Šคํƒ€์ผ)
  2. ์ƒํƒœ ์ „ํ™˜์ด ๋ณต์žกํ•œ ์กฐ๊ฑด ๋ถ„๊ธฐ๋ฌธ์— ๋‚˜์—ด๋˜์–ด ์žˆ์–ด ๊ฐ€๋…์„ฑ์ด ์ข‹์ง€ ์•Š๋‹ค.
  3. ๋ฐ”๋€Œ๋Š” ๋ถ€๋ถ„๋“ค์ด ์บก์Šํ™” ๋˜์–ด์žˆ์ง€ ์•Š์•„ ๋…ธ์ถœ๋˜์–ด์žˆ๋‹ค.
  4. ๋งŒ์ผ ์ƒํƒœ ๊ธฐ๋Šฅ์„ ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•  ๊ฒฝ์šฐ ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ต์งœ๋กœ ์ˆ˜์ •ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, OCP ์›์น™์— ์œ„๋ฐฐ ๋œ๋‹ค.

 

์ƒํƒœ ํŒจํ„ด์„ ์ ์šฉํ•œ ์ฝ”๋“œ โœ”๏ธ

์ƒํƒœ ํŒจํ„ด์˜ ํ•ต์‹ฌ์€ '์ƒํƒœ'๋ฅผ ๊ฐ์ฒดํ™” ํ•˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค. (๊ฐ์ฒด๋ฅผ ์ง€ํ–ฅํ•˜๋ผ)

๋…ธํŠธ๋ถ์˜ ์ƒํƒœ 3๊ฐ€์ง€๋ฅผ ๋ชจ๋‘ ํด๋ž˜์Šค๋กœ ๊ตฌ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋‚˜ ์ถ”์ƒํด๋ž˜์Šค๋กœ ๋ฌถ์–ด ์ถ”์ƒํ™” / ์บก์Šํ™”(์ •๋ณด ์€๋‹‰)๋ฅผ ํ•œ๋‹ค. ์ƒํƒœ๋ฅผ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜์˜€์œผ๋‹ˆ, ์ƒํƒœ์— ๋”ฐ๋ฅธ ํ–‰๋™ ๋ฉ”์†Œ๋“œ๋„ ๊ฐ ์ƒํƒœ ํด๋ž˜์Šค๋งˆ๋‹ค ๊ตฌํ˜„์„ ํ•ด์ค€๋‹ค.

๊ฒฐ๊ณผ์ ์œผ๋กœ ์ฝ”๋“œ์˜ ์ „์ฒด ๋ผ์ธ์ˆ˜๊ฐ€ ๊ธธ์–ด์ง€๊ณ  ๊ดœํžˆ ํด๋ž˜์Šค๋„ ๋•์ง€๋•์ง€ ๋งŽ์•„์ ธ์„œ ์ฝ๊ธฐ ๊ฑฐ๋ถํ•ด ๋ณด์ผ ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์˜คํžˆ๋ ค ์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์ด ๋‚˜์ค‘์— ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์šฉ์ดํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

State-Pattern

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();
    }
}

 

๋ฆฌํŒฉํ† ๋ง ํ•˜๊ธฐ (์‹ฑ๊ธ€ํ†ค ์ ์šฉ) ๐Ÿ‘จ‍๐Ÿ”ง

ํ•˜์ง€๋งŒ ์•„์ง ์œ„์˜ ์ฝ”๋“œ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ๋•Œ ๋งˆ๋‹ค ์ƒˆ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ๋‹ค๋Š” ์ ์ด๋‹ค.

State-Pattern

๋ฌผ๋ก  ์—ฐ๊ฒฐ์ด ๋Š๊ธด ์ƒํƒœ ๊ฐ์ฒด๋Š” JVM์˜ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜(GC) ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ง€์›Œ์ง€๊ฒ ์ง€๋งŒ, ์ด๋Ÿฐ ๊ฐ€๋น„์ง€ ๊ฐ’์ด ๋Š˜์–ด๋‚˜๊ฒŒ๋˜๋ฉด ๋‚˜์ค‘์— ๊ฐ์ฒด ์ œ๊ฑฐ ๊ณผ์ •์—์„œ Stop-the-world ๊ฐ€ ์ผ์–ด๋‚˜๊ฒŒ ๋œ๋‹ค. (ํ”„๋กœ๊ทธ๋žจ์ด ๋ ‰๊ฑธ๋ฆผ)

์™ ๋งŒํ•œ ์ƒํ™ฉ์—์„  ์ƒํƒœ๋Š” ์ƒˆ๋กœ ์ธ์Šคํ„ด์Šคํ™” ํ•  ํ•„์š”๊ฐ€ ์ „ํ˜€ ์—†๋‹ค. ๊ดœํžˆ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„์ธ ์…ˆ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ ์ƒํƒœ ํด๋ž˜์Šค๋“ค์„ ์‹ฑ๊ธ€ํ†ค(Singleton) ํ™” ์‹œํ‚จ๋‹ค.

State-Pattern

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