Design Pattern, State

2022. 2. 16. 20:54ETC/Design Patterns

반응형

 

Object Behavioral Pattern

State Pattern

 

-----------------    INDEX     -----------------

 

State Pattern ?

Strategy vs State

Structure

Sample Code: Java

Known Uses

Pros & Cons

관련 패턴

 

----------------------------------------------

 

 

Allow an object to alter its behavior when its internal state changes.
The object will appear to change its class.


- GoF Design Patterns

 

상태 패턴은 내부 상태가 변경될 때 해당 객체의 행동을 변경시키는 패턴입니다.

 

상태 패턴은 굉장히 단순한 구조이고 개념도 간단합니다.

이름 그대로 상태에 따라 할당되는 상태 객체를 교체한다고 보면 됩니다. 

 

상태 패턴은 모든 상태에 대해 객체를 생성하고 관리합니다.

간단한 예시를 들어볼게요.

 

On/Off 상태를 갖는 스위치 객체가 있을 때, 스위치를 조절하여 불을 켰다 끌 수 있습니다.

간단하게 if 문이나 switch 문을 통해 조절할 수도 있지만, 상태 패턴으로 표현하면 어떻게 될까요?

 

먼저 상태 패턴의 핵심인, 상태를 처리하는 인터페이스와 그의 구현체를 생성합니다.

 

public interface State {
    void toggle(ModeSwitch modeSwitch);
}

class StateLight implements State {
    @Override
    public void toggle(ModeSwitch modeSwitch) {
        System.out.println("Turn off the Switch");
        modeSwitch.setState(new StateDark());
    }
}

class StateDark implements State {
    @Override
    public void toggle(ModeSwitch modeSwitch) {
        System.out.println("Turn on the Switch");
        modeSwitch.setState(new StateLight());
    }
}

 

위와 같이 상태 인터페이스와 그 상태를 구현하는 On(Light)/Off(Dark) 상태를 나타내는 객체를 생성합니다. 

이제 해당 상태를 갖는 객체와 실행하는 클라이언트 코드만을 구현해볼게요.

 

class ModeSwitch {
    private State modeState = new StateDark();

    ModeSwitch() {
        System.out.println("Switch was created.\n");
    }

    void setState(State modeState) {
        this.modeState = modeState;
    }

    void onSwitch() {
        modeState.toggle(this);
    }
}
ModeSwitch modeSwitch = new ModeSwitch();

modeSwitch.onSwitch();
modeSwitch.onSwitch();

 

위와 같이 커진 상태의 스위치를 생성하고 내부 상태 값(modeState)에 따라 다른 객체로 할당받고 있는 것이 보이시나요?

출력은 아래와 같습니다.

 

Switch was created.

Turn on the Switch
Turn off the Switch

 

 

 

Strategy VS State

상태 패턴은 구조적으로 전략 패턴과 매우 유사하고, UML 표현도 비슷합니다.

또한, 두 패턴 모두 런타임으로 객체를 위임해 동작을 변경합니다.

 

두 객체는 구조적인 유사성을 띄고 있지만, 패턴을 사용하는 목적에서 차별성을 보입니다.

따라서, 상태 패턴과 전략 패턴은 구조가 유사하지만 목적성으로 두 패턴을 구별할 수 있습니다.

 

전략 패턴은 위임된 객체를 알고리즘으로 생각하지만,

상태 패턴에서는 위임된 객체를 상태값의 처리로 생각합니다.

 

✔️  Strategy Pattern

| key point |

알고리즘을 교체하고 동작을 변경시키는 것만 생각하며, 상태값에 관심이 없음

 

- 객체를 이용해 적용 알고리즘을 변경

- 지정된 특정 메서드가 모듈화된 모드에 따라 다르게 실행

 

✔️ State Pattern

| key point |

상태값이 매우 중요하며 다음 동작과 객체의 위임을 결정

 

- 동작하는 객체가 상태에 따라 변경

- 이벤트로 발생하는 상태에 따라 객체를 변경

- 상태 메소드가 실행될 때 모드도 전환

 

 

 

Structure

GoF Design Patterns - State

 

Context

- 관심 있는 클라이언트의 인터페이스를 정의합니다.

- 현재 상태를 나타내는 ConcreteState Subclass 인스턴스로 해당 상태를 유지합니다.

 

 

State

- Context의 특정 상태와 관련있는 행위의 캡슐화를 위한 인터페이스를 정의합니다.

 

 

ConcreteState Subclasses

- 각 하위 클래스는 Context의 상태와 관련된 행위를 구현합니다.

 

 

Sample Code: Java

여러 상태를 가지는 객체를 다루는 예시를 들어보려고 합니다.

 

온라인 쇼핑몰에서 물건을 구매할 때, 각 상품에 대한 상태가 계속해서 달라집니다.

상품을 주문하고(ORDER), 그에 대한 가격을 지불하고 (PAY) 나면 주문 완료 상태(ORDERED)로 변경됩니다.

그리고 배송이 다음 절차가 지나고 나면 주문이 완전히 종료(FINISH)됩니다.

 

 

✔️  State

public interface State {
    void process();
}

상태에 적용될 공통된 메서드(handle())를 정의합니다.

 

 

✔️  ConcreteState Subclasses

public class StateOrder implements State {
    @Override
    public void process() {
        System.out.println("주문");
    }
}

public class StatePay implements State {
    @Override
    public void process() {
        System.out.println("결제중");
    }
}

public class StateOrdered implements State {
    @Override
    public void process() {
        System.out.println("주문완료");
    }
}

public class StateFinish implements State {
    @Override
    public void process() {
        System.out.println("처리완료");
    }
}

 

State 인터페이스를 구현하는 서브 클래스들을 정의합니다.

실제 process에는 해당하는 로직을 넣으면 됩니다.

 

 

✔️  Context

public class Order {
    private HashMap<String, State> state;

    public Order() {
        this.state = new HashMap<>() {{
            put("ORDER", new StateOrder());
            put("PAY", new StatePay());
            put("ORDERED", new StateOrdered());
            put("FINISH", new StateFinish());
        }};
    }

    public void process(String status) {
        this.state.get(status).process();
    }
}

 

상태 값에 따라 변경될 객체를 처리하는 handle() 메서드를 구현합니다.

 

✔️  Client

Order order = new Order();
order.process("ORDER");
order.process("PAY");
order.process("ORDERED");
order.process("FINISH");

 

위의 코드를 실행하게 되면 아래와 같은 출력물을 확인할 수 있습니다.

 

 

✔️  Output

주문
결제중
주문완료
처리완료

 

 

 

Known Uses

TCP Connection

상태 패턴을 실제 적용한 분야는 TCP 연결 프로토콜입니다.

Johnson과 Zweig는 상태 패턴을 정의하면서 인터넷 TCP 연결 통신 프로토콜 설계 시 실제 이 패턴을 적용했다고 합니다.

 

Graphic Tools

그래픽 응용 프로그램에도 상태 패턴을 적용합니다.

보통 툴을 선택하면 해당 툴로 계속 그릴 수 있습니다.

 

툴을 선택하는 것이 바로 객체의 상태값을 변경하는 것이고, 변경된 상태값이 유지되는 것과 같습니다.

다른 툴을 선택할 경우 해당되는 상태로 변경이 되며 작업 객체를 변경하는 것으로 볼 수 있습니다.

 

 

 

Pros & Cons

장점

✔️ Single Responsibility Principle

특정 상태와 관련된 코드를 별도의 클래스로 구성합니다.

 

✔️ Open/Closed Principle.

기존 상태 클래스나 Context를 변경하지 않고 새로운 상태를 도입할 수 있습니다.

 

✔️ 조건문 제거
상태에 따른 장황한 조건 분기문을 제거할 수 있기 때문에 Context의 코드를 단순화할 수 있습니다.

 

 

단점
❌  과잉 사용

상태가 적거나 거의 변화가 없다면 패턴을 적용하는 것은 과잉 사용이 될 수 있습니다.

 

 

 

관련 패턴

C- Creational Patterns  |  S - Structural Patterns  |  B - Behavioral Patterns

 

C: Singleton

상태 패턴은 모든 상태에 대해 객체를 생성하고 관리하는데, 새로운 상태 객체를 생성하는 것은 시스템의 메모리 자원을 할당하는 일입니다. 또한, 객체를 생성하는 과정에서 중복된 객체가 생성될 우려도 있습니다.

 

중복된 객체가 생성되는 것을 방지하기 위한 방법으로 싱글턴 패턴이 있습니다.

서브 클래스를 싱글턴으로 변경하여 처리할 수 있습니다.

 

 

B: Strategy

두 객체는 구조적인 유사성을 띄고 있지만, 패턴을 사용하는 목적에서 차별성을 보입니다.

전략 패턴은 위임된 객체를 알고리즘으로 생각하지만,

상태 패턴에서는 위임된 객체를 상태값의 처리로 생각합니다.

 

 

 

 

 

그럼 지금까지 State Pattern에 대해 알아보았습니다.

오타나 잘못된 내용이 있다면 댓글로 남겨주세요!

감사합니다 ☺️ 

 

 

 

모든 Design Patterns 모아보기

 

Design Patterns

안녕하세요. GoF 디자인 패턴을 정리하고자 합니다. 디자인 패턴은 일주일 전부터 공부를 시작했는데, 스스로 설명하듯 적는게 익히는데 도움이 클 것같아 정말 오랜만에 시리즈로 포스팅하려

gngsn.tistory.com

 

반응형

'ETC > Design Patterns' 카테고리의 다른 글

Design Pattern, Prototype  (0) 2022.02.17
Design Pattern, Memento  (0) 2022.02.17
Design Pattern, Visitor  (0) 2022.02.13
Design Pattern, Iterator  (0) 2022.02.10
Design Pattern, Command  (0) 2022.02.06