Gof의 디자인 패턴 - 상태 패턴

Groot·2024년 8월 4일
0

TIL

목록 보기
151/153
post-thumbnail
post-custom-banner

상태 패턴

  • 객체의 내부 상테에 따라 스스로 행동을 변경할 수 있게 허가하는 패턴으로, 이렇게 하면 객체는 마치 자신의 클래스를 바꾸는 것처럼 보인다.

활용성

  • 객체의 행동이 상테에 띠라 달라질 수 있고, 객체의 상태에 따라서 런타임에 행동이 바뀌어야 합니다.
  • 특정 연산에 그 객체의 상테에 따라 달라지는 다중 분기 조건 처리가 너무 많이 들어 있을 때, 객체의 상태를 표현하기 위해 상태를 하나 이상의 enum으로 정의해야 한다. 이때, 객체의 상태를 별도의 객체로 정의하면, 다른 객체들과 상관없이 그 객체의 상태를 댜양화시킬 수 있다.

구조

요소

  • Context : 사용자가 관심 있는 인터페이스를 정의한다. 객체의 현재 상태를 정의한 ConcreteState 서브캘르스의 인스턴스를 유지, 관리한다.
  • State : Context의 각 상태별로 필요한 행동을 캡슐화하여 인터페이스로 정의한다.
  • ConcreteState : 서브클래스, 각 서브클래스들은 Context의 상태에 따라 처리되어야 할 실제 행동을 구현

협력 방법

  • 요청을 받으면 Context는 ConcreteState에게 전달한다 ConcreteState는 State를 상속하는 서브클래스 중 하나의 인스턴스.
  • Context는 실제 연산을 처리할 State 객체에 자신을 매개변수로 전달한다. 이로써 State 객체는 Context 클래스에 정의된 정보에 접근할 수 있다.
  • Context는 사용자가 사용할 수 있는 기본 인터페이스를 제공한다. 사용자는 State 객체를 Context 객체와 연결시킨다. 이렇게 Context 객체를 만들고 나면 사용자는 State 객체를 직접 다루지 않고 Context에 요청만 보내면 된다.

장점

  • 상태에 따른 행동을 국소화하며, 서로 다른 상태에 대한 행동을 별도의 객체로 관리한다.
    • 새로운 상태가 생기면 새로운 클래스만 정의한다.
  • 상태 전이를 명확하게 만든다.
    • 각 상태별로 클래스를 만들기 때문
  • 상태 객체는 공유될 수 있다.
    • State 객체는 인스턴스 변수 없이 여러 Context 클래스의 인스턴스로도 객체를 공유할 수 있다.

단점

  • 과도하게 코드가 많아질 수 있다.

예시 코드

protocol TCPState {
    func transmit(_ connection: TCPConnection)
    func activeOpen(_ connection: TCPConnection)
    func close(_ connection: TCPConnection)
    func changeState(_ connection: TCPConnection, _ state: TCPState)
}

extension TCPState {
    func transmit(_ connection: TCPConnection) {
        // 기본 동작 (필요 시 오버라이드)
    }

    func activeOpen(_ connection: TCPConnection) {
        // 기본 동작 (필요 시 오버라이드)
    }

    func close(_ connection: TCPConnection) {
        // 기본 동작 (필요 시 오버라이드)
    }

    func changeState(_ connection: TCPConnection, _ state: TCPState) {
        connection.setState(state)
    }
}

protocol TCPConnection {
    var state: TCPState { get set }

    func setState(_ state: TCPState)
    func transmit()
    func activeOpen()
    func close()
}

class TCPConnectionImplementation: TCPConnection {
    var state: TCPState
    
    init(initialState: TCPState) {
        self.state = initialState
    }
    
    func setState(_ state: TCPState) {
        self.state = state
    }

    func transmit() {
        state.transmit(self)
    }

    func activeOpen() {
        state.activeOpen(self)
    }

    func close() {
        state.close(self)
    }
}

struct TCPListen: TCPState {
    func transmit(_ connection: TCPConnection) {
        print("TCPListen: transmit 호출됨")
        // TCPListen 상태의 transmit 동작
    }

    func activeOpen(_ connection: TCPConnection) {
        print("TCPListen: activeOpen 호출됨")
        // TCPListen 상태의 activeOpen 동작
    }

    func close(_ connection: TCPConnection) {
        print("TCPListen: close 호출됨")
        // TCPListen 상태의 close 동작
        changeState(connection, TCPClosed())
    }
}

struct TCPEstablished: TCPState {
    func transmit(_ connection: TCPConnection) {
        print("TCPEstablished: transmit 호출됨")
        // TCPEstablished 상태의 transmit 동작
    }

    func activeOpen(_ connection: TCPConnection) {
        print("TCPEstablished: activeOpen 호출됨")
        // TCPEstablished 상태의 activeOpen 동작
    }

    func close(_ connection: TCPConnection) {
        print("TCPEstablished: close 호출됨")
        // TCPEstablished 상태의 close 동작
        changeState(connection, TCPClosed())
    }
}

struct TCPClosed: TCPState {
    func transmit(_ connection: TCPConnection) {
        print("TCPClosed: transmit 호출됨")
        // TCPClosed 상태의 transmit 동작
    }

    func activeOpen(_ connection: TCPConnection) {
        print("TCPClosed: activeOpen 호출됨")
        // TCPClosed 상태의 activeOpen 동작
        changeState(connection, TCPListen())
    }

    func close(_ connection: TCPConnection) {
        print("TCPClosed: close 호출됨")
        // TCPClosed 상태의 close 동작
    }
}

// 사용 예시
let connection = TCPConnectionImplementation(initialState: TCPListen())
connection.activeOpen()  // 현재 상태에 따라 다른 동작 수행
connection.transmit()
connection.close()

참고

profile
I Am Groot
post-custom-banner

0개의 댓글