수색…


소개

유한 상태 기계 개념은 일반적으로 GOF에서 정의 된 상태 패턴을 기반으로 Java 언어 를 사용하는 객체 지향 프로그래밍 (OOP) 언어로 구현됩니다 (책 "디자인 패턴"참조).

R은 객체 지향 패러다임을 시뮬레이트하는 몇 가지 메커니즘을 제공하며,이 패턴을 구현하기 위해 S4 Object System 을 적용 해 보겠습니다.

상태 머신을 사용한 라인 구문 분석

R의 S4 Class 기능을 사용하여 특정 패턴을 사용하여 선을 파싱하는 데 State Machine 패턴 을 적용 해 보겠습니다.

문제 해결

구분 기호 ( ";" )를 사용하여 각 행에서 사람에 대한 정보를 제공하는 파일을 구문 분석해야하지만 제공된 일부 정보는 선택 사항이며 빈 필드를 제공하는 대신 누락되었습니다. 각 줄에는 다음과 같은 정보가 있습니다 : Name;[Address;]Phone . 주소 정보가 선택 사항 인 경우, 때로는 주소 정보가 있고 때로는 사용하지 않는 경우가 있습니다. 예를 들면 다음과 같습니다.

GREGORY BROWN; 25 NE 25TH; +1-786-987-6543
DAVID SMITH;786-123-4567
ALAN PEREZ; 25 SE 50TH; +1-786-987-5553

두 번째 행은 주소 정보를 제공하지 않습니다. 따라서 분리 문자의 수는이 경우에는 하나의 분리 문자로, 다른 행의 경우에는 두 개의 분리 문자로 다를 수 있습니다. 구분 기호의 수가 달라질 수 있으므로이 문제를 해결하는 한 가지 방법은 패턴을 기반으로 주어진 필드의 존재 여부를 인식하는 것입니다. 그러한 경우 우리는 이러한 패턴을 식별하기 위해 정규식 을 사용할 수 있습니다. 예 :

  • 이름 : "^([AZ]'?\\s+)* *[AZ]+(\\s+[AZ]{1,2}\\.?,? +)*[AZ]+((-|\\s+)[AZ]+)*$" . 예 : RAFAEL REAL, DAVID R. SMITH, ERNESTO PEREZ GONZALEZ, 0' CONNOR BROWN, LUIS PEREZ-MENA
  • 주소 : "^\\s[0-9]{1,4}(\\s+[AZ]{1,2}[0-9]{1,2}[AZ]{1,2}|[AZ\\s0-9]+)$" . 예 : 11020 LE JEUNE ROAD , 87 SW 27TH . 간단히하기 위해 여기에는 우편 번호, 도시, 주를 포함하지 않지만이 필드에 추가하거나 필드를 추가 할 수 있습니다.
  • 전화 : "^\\s*(\\+1(-|\\s+))*[0-9]{3}(-|\\s+)[0-9]{3}(-|\\s+)[0-9]{4}$" 입니다. 예 : 305-123-4567, 305 123 4567, +1-786-123-4567 .

참고 사항 :

  • 나는 미국 주소와 전화의 가장 일반적인 패턴을 고려 중이며,보다 일반적인 상황을 고려하기 쉽습니다.
  • R에서 기호 "\" 는 문자 변수에 대해 특별한 의미를 가지므로이를 이스케이프 처리해야합니다.
  • 정규 표현식을 정의하는 과정을 단순화하기 위해 다음 웹 페이지 regex101.com 을 사용하는 것이 좋습니다. 모든 가능한 조합에 대해 예상되는 결과를 얻을 때까지 주어진 예제를 가지고 재생할 수 있습니다.

아이디어는 이전에 정의 된 패턴을 기반으로 각 라인 필드를 식별하는 것입니다. State 패턴은 특정 동작을 제어하기 위해 협업하는 다음 엔티티 (클래스)를 정의합니다 (State Pattern은 동작 패턴 임).

GOF 상태 패턴

문제의 맥락을 고려하여 각 요소를 설명해 보겠습니다.

  • Context : 구문 분석 프로세스의 컨텍스트 정보, 즉 현재 상태를 저장하고 전체 상태 시스템 프로세스를 처리합니다. 각 상태에 대해 액션이 실행되지만 ( handle() ), 컨텍스트는 특정 상태 ( handle()State 클래스에서 정의 된 동작 메서드)에서 상태를 기반으로 컨텍스트를 위임합니다. 그것은 클라이언트에게 관심있는 인터페이스를 정의합니다. Context 클래스는 다음과 같이 정의 할 수 있습니다.
    • 속성 : state
    • 메소드 : handle() , ...
  • State : 상태 시스템의 모든 상태를 나타내는 추상 클래스입니다. 컨텍스트의 특정 상태와 관련된 비헤이비어를 캡슐화하기위한 인터페이스를 정의합니다. 다음과 같이 정의 할 수 있습니다.
    • 속성 : name, pattern
    • 메소드 : doAction() , isState ( pattern 속성을 사용하여 입력 인자가이 상태 패턴에 속하는지 아닌지 확인), ...
  • Concrete States (상태 하위 클래스) : Context 상태와 연관된 비헤이비어를 구현하는 클래스 State 의 각 하위 클래스입니다. 하위 클래스는 InitState , NameState , AddressState , PhoneState 입니다. 이러한 클래스는 이러한 상태에 대한 특정 논리를 사용하는 일반 메소드를 구현합니다. 추가 속성은 필요하지 않습니다.

주의 : action, handle() , doAction() 또는 goNext() 를 수행하는 메소드의 이름을 지정하는 것이 선호됩니다. 메소드 이름 doAction() 은 입력 인자가 같고 클래스가 다른 두 개의 일반적인 메소드를 정의 할 때 혼란을 피하기 위해 Context 클래스에서 handle() 으로 이름을 지정하기를 원하는 두 클래스 ( State 또는 Context )에서 모두 동일 할 수 있습니다.

사용자 클래스

S4 구문을 사용하여 다음과 같이 Person 클래스를 정의 할 수 있습니다.

setClass(Class = "Person",
    slots = c(name = "character", address = "character", phone = "character")
)

클래스 속성을 초기화하는 것이 좋습니다. setClass 문서에서는 prototype, representation 과 같이 사용되지 않는 속성을 사용하는 대신 "initialize" 라는 레이블이 붙은 일반 메소드를 사용하도록 제안합니다.

setMethod("initialize", "Person",
  definition = function(.Object, name = NA_character_,
    address = NA_character_, phone = NA_character_) {
        .Object@name <- name
        .Object@address <- address
        .Object@phone <- phone
        .Object
    }
)

initialize 메소드는 이미 패키지 methods 의 표준 제네릭 메소드이기 때문에 원래 인수 정의를 존중해야합니다. R 프롬프트에서 입력 내용을 확인할 수 있습니다.

> initialize

전체 함수 정의를 반환합니다. 함수 정의가 맨 위에 표시됩니다.

function (.Object, ...) {...}

따라서 setMethod 를 사용할 때 exaclty 와 동일한 구문 ( .Object )을 따라야합니다.

기존의 또 다른 일반적인 방법은 show , Java의 toString() 메소드와 동일하며 클래스 도메인에 대한 특정 구현을 갖는 것이 좋습니다.

setMethod("show", signature = "Person",
  definition = function(object) {
      info <- sprintf("%s@[name='%s', address='%s', phone='%s']", 
        class(object), object@name, object@address, object@phone)
      cat(info)
      invisible(NULL)
  }
)

: 우리는 기본 toString() Java 구현과 동일한 규칙을 사용합니다.

파싱 ​​된 정보 ( Person 객체의 목록)를 데이터 세트에 저장하고 싶다면 먼저 객체 목록을 R이 변형 할 수있는 것으로 변환 할 수 있어야합니다 (예 : 객체를 목록으로 강제 변환). 다음과 같은 추가 메서드를 정의 할 수 있습니다 (이에 대한 자세한 내용은 게시물 참조).

setGeneric(name = "as.list", signature = c('x'),
    def = function(x) standardGeneric("as.list"))

# Suggestion taken from here:
# http://stackoverflow.com/questions/30386009/how-to-extend-as-list-in-a-canonical-way-to-s4-objects
setMethod("as.list", signature = "Person",
    definition = function(x) {
        mapply(function(y) {
        #apply as.list if the slot is again an user-defined object
        #therefore, as.list gets applied recursively
        if (inherits(slot(x,y),"Person")) {
          as.list(slot(x,y))
        } else {
          #otherwise just return the slot
          slot(x,y)
        }
      },
        slotNames(class(x)),
        SIMPLIFY=FALSE)
    }
)

R은 OO에 대한 설탕 구문을 제공하지 않습니다. 왜냐하면이 언어는 처음에는 Statisticians에게 가치있는 기능을 제공하기 위해 고안 되었기 때문입니다. 그러므로 각 사용자 메소드는 1) 정의 부분 ( setGeneric 통해)과 2) 구현 부분 ( setMethod 를 통해)의 두 부분을 필요로합니다. 위의 예와 같습니다.

주급

S4 구문에 따라, 추상 State 클래스를 정의합시다.

setClass(Class = "State", slots = c(name = "character", pattern = "character"))

setMethod("initialize", "State",
  definition = function(.Object, name = NA_character_, pattern = NA_character_) {
      .Object@name <- name
      .Object@pattern <- pattern
      .Object
  }
)

setMethod("show", signature = "State",
  definition = function(object) {
      info <- sprintf("%s@[name='%s', pattern='%s']", class(object), 
          object@name, object@pattern)
      cat(info)
      invisible(NULL)
  }
)

setGeneric(name = "isState", signature = c('obj', 'input'),
    def = function(obj, input) standardGeneric("isState"))

setGeneric(name = "doAction", signature = c('obj', 'input', 'context'),
    def = function(obj, input, context) standardGeneric("doAction"))

State 모든 하위 클래스는 namepattern 관련 지을뿐만 아니라 지정된 입력이이 상태에 속하는지 여부를 식별하는 방법 ( isState() 메서드)과이 상태 ( doAction() 메서드 doAction() 방법).

프로세스를 이해하기 위해 수신 된 입력을 기반으로 각 상태에 대한 전이 행렬을 정의합시다.

입력 / 현재 상태 초기화 이름 주소 전화
이름 이름
주소 주소
전화 전화 전화
종료 종료

주 :[row, col]=[i,j] 는 입력 i 수신 할 때 현재 상태 j 의 대상 상태를 나타냅니다.

그것은 상태 Name에서 두 개의 입력 (주소 또는 전화 번호)을 수신 할 수 있음을 의미합니다. 트랜잭션 테이블을 나타내는 또 다른 방법은 다음 UML State Machine 다이어그램을 사용하는 것입니다.

상태 머신 다이어그램 표현

의이 클래스의 하위 상태로 각각의 특정 상태를 구현하자 State

주 하위 클래스

초기화 상태 :

초기 상태는 다음 클래스를 통해 구현됩니다.

setClass("InitState", contains = "State")

setMethod("initialize", "InitState",
  definition = function(.Object, name = "init", pattern = NA_character_) {
      .Object@name <- name
      .Object@pattern <- pattern
      .Object
  }
)

setMethod("show", signature = "InitState",
  definition = function(object) {
      callNextMethod()
  }
)

R에서 클래스가 다른 클래스의 하위 클래스임을 나타내려면 contains 속성을 사용하고 상위 클래스의 클래스 이름을 나타냅니다.

하위 클래스는 추가 속성을 추가하지 않고 일반 메서드를 구현하기 때문에 show 메서드는 상위 클래스에서 동일한 메서드를 호출하기 callNextMethod() 메서드를 통해).

초기 상태는 패턴과 관련이 없으며 단지 프로세스의 시작을 나타냅니다. 그런 다음 클래스를 NA 값으로 초기화합니다.

이제 State 클래스에서 제네릭 메서드를 구현할 수 있습니다.

setMethod(f = "isState", signature = "InitState",
  definition = function(obj, input) {
      nameState <- new("NameState")
      result <- isState(nameState, input)
      return(result)
  }
)

이 특정 상태 ( pattern )의 경우, 첫 번째 필드를 예상하는 구문 분석 프로세스를 초기화하는 아이디어는 name 이 될 것이며, 그렇지 않으면 오류가 발생합니다.

setMethod(f = "doAction", signature = "InitState",
    definition = function(obj, input, context) {
        nameState <- new("NameState")
        if (isState(nameState, input)) {
            person <- context@person
            person@name <- trimws(input)
            context@person <- person
            context@state <- nameState
        } else {
            msg <- sprintf("The input argument: '%s' cannot be identified", input)
            stop(msg)
        }
        return(context)
    }
)

doAction 메서드는 전환을 제공하고 추출 된 정보로 컨텍스트를 업데이트합니다. 여기서는 @-operator 를 통해 컨텍스트 정보에 액세스합니다. 대신이 프로세스를 캡슐화하기 get/set 메소드를 정의 할 get/set 있지만 (OO 베스트 프랙티스 : 캡슐화에서 필수적이므로),이 예제의 목적을 위해 값을 추가하지 않고 get-set 당 네 가지 메소드를 추가합니다.

모든 doAction 구현에서 입력 인수가 제대로 식별되지 않을 때 보호 장치를 추가하는 것이 좋습니다.

이름 주

이 클래스 정의의 정의는 다음과 같습니다.

setClass ("NameState", contains = "State")

setMethod("initialize","NameState",
  definition=function(.Object, name="name",
        pattern = "^([A-Z]'?\\s+)* *[A-Z]+(\\s+[A-Z]{1,2}\\.?,? +)*[A-Z]+((-|\\s+)[A-Z]+)*$") {
        .Object@pattern <- pattern
        .Object@name <- name
        .Object       
  }
)

setMethod("show", signature = "NameState",
  definition = function(object) {
      callNextMethod()
  }
)

입력이 주어진 패턴에 속하는지 확인하기 위해 grepl 함수를 사용합니다.

setMethod(f="isState", signature="NameState",
  definition=function(obj, input) {
      result <- grepl(obj@pattern, input, perl=TRUE)
      return(result)
  }
)

이제 주어진 상태에 대해 수행 할 동작을 정의합니다.

setMethod(f = "doAction", signature = "NameState",
  definition=function(obj, input, context) {
      addressState <- new("AddressState")
      phoneState <- new("PhoneState")
      person <- context@person
      if (isState(addressState, input)) {
          person@address <- trimws(input)
          context@person <- person
          context@state <- addressState
      } else if (isState(phoneState, input)) {
          person@phone <- trimws(input)
          context@person <- person
          context@state <- phoneState
      } else {
          msg <- sprintf("The input argument: '%s' cannot be identified", input)
          stop(msg)
      }
      return(context)
  }
)

여기서는 가능한 전환을 고려합니다. 하나는 주소 상태이고 다른 하나는 전화 상태입니다. 모든 경우에 컨텍스트 정보를 업데이트합니다.

  • person 정보 : 입력 인수가있는 address 또는 phone .
  • 프로세스 state

상태를 식별하는 방법은 특정 상태에 대한 isState() 메소드를 호출하는 것입니다. 기본 특정 상태 ( addressState, phoneState )를 만든 다음 특정 유효성 검사를 요청합니다.

다른 하위 클래스 (상태 당 하나) 구현 로직은 매우 유사합니다.

주소 상태

setClass("AddressState", contains = "State")

setMethod("initialize", "AddressState",
  definition = function(.Object, name="address",
    pattern = "^\\s[0-9]{1,4}(\\s+[A-Z]{1,2}[0-9]{1,2}[A-Z]{1,2}|[A-Z\\s0-9]+)$") {
        .Object@pattern <- pattern
        .Object@name <- name
        .Object
    }
)

setMethod("show", signature = "AddressState",
  definition = function(object) {
      callNextMethod()
  }
)

setMethod(f="isState", signature="AddressState",
    definition=function(obj, input) {
        result <- grepl(obj@pattern, input, perl=TRUE)
        return(result)
    }
)
    
setMethod(f = "doAction", "AddressState",
    definition=function(obj, input, context) {
        phoneState <- new("PhoneState")
        if (isState(phoneState, input)) {
            person <- context@person
            person@phone <- trimws(input)
            context@person <- person
            context@state <- phoneState
        } else {
            msg <- sprintf("The input argument: '%s' cannot be identified", input)
            stop(msg)
        }
        return(context)
    }
)

전화 상태

setClass("PhoneState", contains = "State")

setMethod("initialize", "PhoneState",
  definition = function(.Object, name = "phone",
    pattern = "^\\s*(\\+1(-|\\s+))*[0-9]{3}(-|\\s+)[0-9]{3}(-|\\s+)[0-9]{4}$") {
        .Object@pattern <- pattern
        .Object@name <- name
        .Object
    }
)

setMethod("show", signature = "PhoneState",
  definition = function(object) {
      callNextMethod()
  }
)

setMethod(f = "isState", signature = "PhoneState",
    definition = function(obj, input) {
        result <- grepl(obj@pattern, input, perl = TRUE)
        return(result)
    }
)

여기에 사람 정보를 contextpersons 목록에 추가하는 부분이 있습니다.

setMethod(f = "doAction", "PhoneState",
    definition = function(obj, input, context) {
        context <- addPerson(context, context@person)
        context@state <- new("InitState")
        return(context)
    }   
)

문맥 클래스

이제는 Context 클래스 구현을 설명 할 수 있습니다. 다음 속성을 고려하여 정의 할 수 있습니다.

setClass(Class = "Context",
     slots = c(state = "State", persons = "list", person = "Person")
)

어디에

  • state : 프로세스의 현재 상태
  • person : 현재 사람은 이미 현재 줄에서 파싱 한 정보를 나타냅니다.
  • persons : 처리 된 파싱 된 사람들의 목록.

: 선택적으로 둘 이상의 구문 분석기 유형으로 작업 할 경우 name 을 사용하여 컨텍스트를 식별하는 name 을 추가 할 수 있습니다.

setMethod(f="initialize", signature="Context",
  definition = function(.Object) {
        .Object@state <- new("InitState")
        .Object@persons <- list()
        .Object@person <- new("Person")
        return(.Object)
    }
)

setMethod("show", signature = "Context",
  definition = function(object) {
      cat("An object of class ", class(object), "\n", sep = "")
      info <- sprintf("[state='%s', persons='%s', person='%s']", object@state,
          toString(object@persons), object@person)
      cat(info)
      invisible(NULL)
  }
)

setGeneric(name = "handle", signature = c('obj', 'input', 'context'),
    def = function(obj, input, context) standardGeneric("handle"))

setGeneric(name = "addPerson", signature = c('obj', 'person'),
    def = function(obj, person) standardGeneric("addPerson"))

setGeneric(name = "parseLine", signature = c('obj', 's'),
    def = function(obj, s) standardGeneric("parseLine"))

setGeneric(name = "parseLines", signature = c('obj', 's'),
    def = function(obj, s) standardGeneric("parseLines"))

setGeneric(name = "as.df", signature = c('obj'),
    def = function(obj) standardGeneric("as.df"))

이러한 일반적인 메서드를 사용하여 구문 분석 프로세스의 전체 동작을 제어합니다.

  • handle() : 현재 state 의 특정 doAction() 메소드를 호출합니다.
  • addPerson : 일단 최종 상태에 도달하면 구문 분석 한 persons 목록에 person 을 추가해야합니다.
  • parseLine() : 한 줄을 파싱합니다.
  • parseLines() : 여러 줄을 파싱합니다 (줄의 배열)
  • as.df() : persons 목록의 정보를 데이터 프레임 객체로 추출합니다.

이제 구현에 대해 알아 보겠습니다.

handle() 메서드는 context 의 현재 state 에서 doAction() 메서드에 위임합니다.

setMethod(f = "handle", signature = "Context",
    definition = function(obj, input) {
        obj <- doAction(obj@state, input, obj)
        return(obj)
    }
)

setMethod(f = "addPerson", signature = "Context",
  definition = function(obj, person) {
      obj@persons <- c(obj@persons, person)
      return(obj)
  }
)

먼저, R- 함수 strsplit() 통해 각 요소를 식별하기 위해 구분 기호를 사용하여 배열의 원래 줄을 분할 한 다음 각 요소를 주어진 상태에 대한 입력 값으로 반복합니다. handle() 메서드는 업데이트 된 정보 ( state , person , persons 속성)가있는 context 를 다시 반환합니다.

setMethod(f = "parseLine", signature = "Context",
  definition = function(obj, s) {
      elements <- strsplit(s, ";")[[1]]
      # Adding an empty field for considering the end state.
      elements <- c(elements, "")
      n <- length(elements)
      input <- NULL
      for (i in (1:n)) {
        input <- elements[i]  
        obj <- handle(obj, input)
      }
      return(obj@person)
  }
)

R이 입력 인자의 복사본을 만들면, 우리는 컨텍스트 ( obj )를 반환 할 필요가있다 :

setMethod(f = "parseLines", signature = "Context",
  definition = function(obj, s) {
      n <- length(s)
      listOfPersons <- list()
      for (i in (1:n)) {
          ipersons <- parseLine(obj, s[i])
          listOfPersons[[i]] <- ipersons
      }
      obj@persons <- listOfPersons
      return(obj)
  }
)

속성 persons S4의 인스턴스의 목록입니다 Person 클래스입니다. R은 사용자 정의 클래스의 인스턴스를 처리하지 못하기 때문에이 표준은 어떤 표준 유형으로도 강제 변환 될 수 없습니다. 해결책은 이전에 정의 된 as.list 메소드를 사용하여 Person 을 목록으로 변환하는 것입니다. 그런 다음 우리는리스트의 각 요소에이 기능을 적용 할 수있는 persons 의 비아, lapply() 함수입니다. 그 다음에 다음의 호출에 lappy() 함수, 현재 적용 data.frame 의 각 요소에 대해 변환 함수를 persons.list 데이터 프레임으로. 마지막으로 rbind() 함수는 생성 된 데이터 프레임의 새로운 행으로 변환 된 각 요소를 추가하기 위해 호출됩니다 (이에 대한 자세한 내용은이 게시물 참조)

# Sugestion taken from this post:
# http://stackoverflow.com/questions/4227223/r-list-to-data-frame
setMethod(f = "as.df", signature = "Context",
  definition = function(obj) {
    persons <- obj@persons
    persons.list <- lapply(persons, as.list)
    persons.ds <- do.call(rbind, lapply(persons.list, data.frame, stringsAsFactors = FALSE))
    return(persons.ds)
  }
)

함께 모든 것을 뿌리기

마지막으로 전체 솔루션을 테스트 할 수 있습니다. 주소 정보가 누락 된 두 번째 행의 구문 분석 할 줄을 정의하십시오.

s <- c(
    "GREGORY BROWN; 25 NE 25TH; +1-786-987-6543",
    "DAVID SMITH;786-123-4567",
     "ALAN PEREZ; 25 SE 50TH; +1-786-987-5553"
)

이제 context 를 초기화하고 행을 구문 분석합니다.

context <- new("Context")
context <- parseLines(context, s)

마지막으로 해당 데이터 집합을 가져 와서 인쇄하십시오.

df <- as.df(context)
> df
           name    address           phone
1 GREGORY BROWN 25 NE 25TH +1-786-987-6543
2   DAVID SMITH       <NA>    786-123-4567
3    ALAN PEREZ 25 SE 50TH +1-786-987-5553

이제 show 메소드를 테스트 해 봅시다.

> show(context@persons[[1]])
Person@[name='GREGORY BROWN', address='25 NE 25TH', phone='+1-786-987-6543']

그리고 일부 하위 주 :

>show(new("PhoneState"))
PhoneState@[name='phone', pattern='^\s*(\+1(-|\s+))*[0-9]{3}(-|\s+)[0-9]{3}(-|\s+)[0-9]{4}$']

마지막으로 as.list() 메서드를 테스트합니다.

> as.list(context@persons[[1]])
$name
[1] "GREGORY BROWN"

$address
[1] "25 NE 25TH"

$phone
[1] "+1-786-987-6543"

> 

결론

이 예제는 OO 패러다임을 사용하기 위해 R에서 사용 가능한 메커니즘 중 하나를 사용하여 State 패턴을 구현하는 방법을 보여줍니다. 그럼에도 불구하고 R OO 솔루션은 사용자에게 친숙하지 않으며 다른 OOP 언어와 많이 다릅니다. 구문이 완전히 다르기 때문에 사고 방식을 전환해야합니다. 기능적 프로그래밍 패러다임을 상기시켜줍니다. 예를 들어 : object.setID("A1") 대신 Java / C # 에서처럼 R의 경우 setID(object, "A1") 와 같은 방법으로 메소드를 호출해야합니다. 따라서 함수의 컨텍스트를 제공하기 위해 항상 입력 인수로 객체를 포함해야합니다. 같은 방법 this 클래스 속성과 "." 지정된 클래스의 메서드 또는 속성에 액세스하기위한 표기법. 클래스 또는 메소드를 참조하는 것은 속성 값 ( "Person" , "isState" 등)을 통해 수행되기 때문에 더 많은 오류 프롬프트입니다.

위의 S4 클래스 솔루션은 간단한 작업을 수행하는 데 필요한 기존 Java / C # 언어보다 훨씬 많은 코드 라인을 필요로합니다. 어쨌든, State Pattern은 이러한 종류의 문제에 대한 훌륭하고 일반적인 솔루션입니다. 논리를 특정 상태로 위임하는 프로세스를 단순화합니다. 모든 상황을 제어하기위한 큰 if-else 블록을 갖는 대신, 각 상태에서 수행 할 작업을 구현하기 위해 각 State 하위 클래스 구현에있는 더 작은 if-else 블록을 사용합니다.

첨부 파일 : 여기 당신은 전체 스크립트를 다운로드 할 수 있습니다.

어떤 제안이라도 환영합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow