[Design Pattern] State Pattern(상태 패턴)

Main·2024년 8월 21일
0

Design Pattern

목록 보기
6/7

State Pattern이란 ?

상태 패턴은 객체의 상태에 따라 행동을 변경하는 디자인 패턴입니다. 조건문으로 특정 상태를 변경하는 것이 아닌 상태를 객체화하여 필요에 따라 다르게 행동하도록 위임합니다.


💡 State ?

State란 객체가 가질 수 있는 어떤 조건이나 상황을 나타냅니다.

예를 들어 자동차에 시동이 걸려있는 상태라면 속력을 올리거나 줄일 수 있습니다. 만약 자동차의 시동이 꺼지는 상태라면 속력을 올리거나 줄일 수 없습니다. 자동차 시동 상태에 따라 메소드가 달라집니다.

State_Pattern.png


State 패턴의 구성 요소

  • Context : 상태 전환을 관리하는 클래스로 현재 상태를 유지하고 있으며, 상태에 따라 행동을 위임합니다.
  • State : 상태를 나타내는 인터페이스 혹은 추상 클래스로 상태별로 다른 행동을 정의합니다.
  • ConcreteState : 특정 상태를 나타내는 클래스로 State 인터페이스를 구현하고, 각 상태에 따른 행위를 정의합니다.

State 패턴의 장점

  • 유지 보수 용이 : 상태에 따른 동작을 개별 클래스로 옮겨서 관리 할 수 있어 유지 보수가 용이합니다.
  • 코드 간결성 : 상태와 관련된 모든 동작을 각각의 클래스에 분산시켜 코드가 간결해집니다.
  • 단일 책임 원칙 준수 : 단일 책임 원칙을 준수 할 수 있습니다.
  • 개방 폐쇄 원칙 준수 : 개방 폐쇄의 원칙을 준수 할 수 있습니다.
  • 일관성 유지 : 하나의 상태 객체만 사용하여 상태 변경을 하므로 일관성 없는 상태 주입을 방지할 수 있습니다.

State 패턴의 단점

  • 클래스 수 증가 : 상태가 많아질수록 클래스가 증가하게 되어, 전체 시스템의 복잡성이 증가할 수 있습니다
  • 상태 관리의 복잡성 : 상태가 서로 의존하거나 복잡한 전이가 필요한 경우, 이를 관리하는 것이 어려워질 수 있습니다.
  • 상태 전이 로직 : 상태 전이 로직이 복잡해질 경우, 이를 관리하는 코드가 길어져 가독성이 떨어질 수 있습니다.

State 패턴 구현 예시 코드

State 패턴을 이용하여 신호등을 구현하는 간단한 예시코드입니다.

TrafficLightContext.ts
현재 상태(TrafficLightState)를 유지하고, 상태 전환 요청(changeLight)을 처리합니다.

import { TrafficLightState } from "./TrafficLightState";

// Context 클래스: 현재 신호등 상태를 관리하는 클래스
export class TrafficLightContext {
  private state: TrafficLightState;

  constructor(state: TrafficLightState) {
    this.state = state;
  }

  public setState(state: TrafficLightState): void {
    this.state = state;
  }

  public renderCurrentTraffic() {
    this.state.renderTraffic();
  }

  public changeState(): void {
    this.state.changeLight(this);
  }
}

TrafficLightState.ts

상태의 인터페이스로, 모든 구체적인 상태 클래스는 이 인터페이스를 구현합니다.

import { TrafficLightContext } from "./TrafficLightContext";

// State 인터페이스
export interface TrafficLightState {
  changeLight(context: TrafficLightContext): void;
  renderTraffic(): void;
}

RedLight.ts

changeLight 메서드를 통해 상태를 변경합니다.

renderTraffic 메서드를 통해 UI를 렌더링합니다.

빨간불 ⇒ 초록불로 상태를 변경합니다.

import { GreenLight } from "./GreenLight";
import { TrafficLightContext } from "./TrafficLightContext";
import { TrafficLightState } from "./TrafficLightState";

// ConcreteState 클래스 - 빨간불
export class RedLight implements TrafficLightState {
  private static instance: RedLight;

  public static getInstance(): RedLight {
    if (!RedLight.instance) {
      RedLight.instance = new RedLight();
    }
    return RedLight.instance;
  }

  public renderTraffic() {
    const redTraffic = document.querySelector(".red")!;
    const yellowTraffic = document.querySelector(".yellow")!;
    const greenTraffic = document.querySelector(".green")!;
    const message = document.querySelector(".message")!;

    yellowTraffic.classList.remove("active");
    greenTraffic.classList.remove("active");
    redTraffic.classList.add("active");
    message.textContent = "빨간불: 차량 정지!";
  }

  public changeLight(context: TrafficLightContext): void {
    context.setState(GreenLight.getInstance());
  } 
}

YellowLight.ts

changeLight 메서드를 통해 상태를 변경합니다.

renderTraffic 메서드를 통해 UI를 렌더링합니다.

노란불 ⇒ 빨간불로 상태를 변경합니다.

import { RedLight } from './RedLight';
import { TrafficLightContext } from "./TrafficLightContext";
import { TrafficLightState } from "./TrafficLightState";

// ConcreteState 클래스 - 노란불
export class YellowLight implements TrafficLightState {
  private static instance: YellowLight;

  static getInstance(): YellowLight {
    if (!YellowLight.instance) {
      YellowLight.instance = new YellowLight();
    }
    return YellowLight.instance;
  }

  public renderTraffic() {
    const redTraffic = document.querySelector(".red")!;
    const yellowTraffic = document.querySelector(".yellow")!;
    const greenTraffic = document.querySelector(".green")!;
    const message = document.querySelector(".message")!;

    redTraffic.classList.remove("active");
    greenTraffic.classList.remove("active");
    yellowTraffic.classList.add("active");
    message.textContent = "노란불: 신호 변경 주의!";
  }

  public changeLight(context: TrafficLightContext): void {
    context.setState(RedLight.getInstance());
  }
}

GreenLight.ts

changeLight 메서드를 통해 상태를 변경합니다.

renderTraffic 메서드를 통해 UI를 렌더링합니다.

초록불 ⇒ 노란불로 상태를 변경합니다.

import { TrafficLightContext } from "./TrafficLightContext";
import { TrafficLightState } from "./TrafficLightState";
import { YellowLight } from "./YellowLight";

// ConcreteState 클래스 - 초록불
export class GreenLight implements TrafficLightState {
  private static instance: GreenLight;

  public static getInstance(): GreenLight {
    if (!GreenLight.instance) {
      GreenLight.instance = new GreenLight();
    }
    return GreenLight.instance;
  }

  public renderTraffic() {
    const redTraffic = document.querySelector(".red")!;
    const yellowTraffic = document.querySelector(".yellow")!;
    const greenTraffic = document.querySelector(".green")!;
    const message = document.querySelector(".message")!;

    yellowTraffic.classList.remove("active");
    redTraffic.classList.remove("active");
    greenTraffic.classList.add("active");
    message.textContent = "초록불: 차량 출발";
  }

  public changeLight(context: TrafficLightContext): void {
    context.setState(YellowLight.getInstance());
  }
}

State_Pattern2.png

  • Context는 현재 상태를 유지하며, changeState 메서드를 호출하여 상태를 변경하며, renderCurrentTraffic 메서드를 호출하여 상태에 따른 UI를 업데이트합니다.
  • State 인터페이스는 changeLight, renderTraffic 메서드를 정의하고, 구체적인 상태 클래스들(RedLight, YellowLight, GreenLight)이 이 메서드를 구현합니다.
  • 상태가 변경될 때마다, Context는 새로운 상태를 설정하고, 해당 상태의 renderTraffic 메서드를 호출하여 UI를 업데이트합니다.

정리

State 패턴은 객체의 상태에 따라 동작을 달리하는 디자인 패턴으로, 조건문 대신 상태를 객체로 캡슐화하여 관리합니다. 이를 통해 코드의 가독성과 유지보수성이 향상됩니다. 각 상태는 자신의 동작을 관리하며, 상태 전이는 명확하게 정의됩니다. 그러나 상태가 많아지면 클래스가 증가할 수 있어 복잡성이 커질 수 있습니다.


참고 사이트

https://inpa.tistory.com/entry/GOF-💠-상태State-패턴-제대로-배워보자

profile
함께 개선하는 프론트엔드 개발자

0개의 댓글