Suche…


Einführung

Finite-States-Machine- Konzepte werden normalerweise in OOP-Sprachen (Object Oriented Programming) implementiert, z. B. in Java, basierend auf dem in GOF definierten State-Pattern (verweist auf das Buch: "Design Patterns").

R bietet mehrere Mechanismen, um das OO-Paradigma zu simulieren. Wenden wir das S4-Objektsystem an, um dieses Muster zu implementieren.

Zeilen mit State Machine analysieren

Wenden wir das State Machine-Muster für das Analysieren von Zeilen mit dem spezifischen Muster mithilfe der S4-Klassenfunktion von R an.

PROBLEM ENUNCIATION

Wir müssen eine Datei analysieren, in der jede Zeile Informationen über eine Person enthält, wobei ein Trennzeichen ( ";" ) verwendet wird. Einige Angaben sind jedoch optional. Statt ein leeres Feld anzugeben, fehlt es. In jeder Zeile stehen folgende Informationen zur Verfügung: Name;[Address;]Phone . Wo die Adressinformationen optional sind, haben wir sie manchmal und manchmal nicht, zum Beispiel:

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

Die zweite Zeile enthält keine Adressinformationen. Daher kann die Anzahl der Trennzeichen unterschiedlich sein, wie in diesem Fall mit einem Trennzeichen und für die anderen Zeilen mit zwei Trennzeichen. Da die Anzahl der Trennzeichen variieren kann, besteht eine Möglichkeit, dieses Problem zu lösen, darin, das Vorhandensein oder Nichtvorhandensein eines bestimmten Feldes anhand seines Musters zu erkennen. In diesem Fall können wir einen regulären Ausdruck verwenden, um solche Muster zu identifizieren. Zum Beispiel:

  • Name : "^([AZ]'?\\s+)* *[AZ]+(\\s+[AZ]{1,2}\\.?,? +)*[AZ]+((-|\\s+)[AZ]+)*$" . Zum Beispiel: RAFAEL REAL, DAVID R. SMITH, ERNESTO PEREZ GONZALEZ, 0' CONNOR BROWN, LUIS PEREZ-MENA usw.
  • Adresse : "^\\s[0-9]{1,4}(\\s+[AZ]{1,2}[0-9]{1,2}[AZ]{1,2}|[AZ\\s0-9]+)$" . Zum Beispiel: 11020 LE JEUNE ROAD , 87 SW 27TH . Der Einfachheit halber haben wir hier nicht die Postleitzahl, Stadt, Bundesland, aber ich kann in dieses Feld aufgenommen werden oder zusätzliche Felder hinzufügen.
  • Telefon : "^\\s*(\\+1(-|\\s+))*[0-9]{3}(-|\\s+)[0-9]{3}(-|\\s+)[0-9]{4}$" . Zum Beispiel: 305-123-4567, 305 123 4567, +1-786-123-4567 .

Anmerkungen :

  • Ich denke über das häufigste Muster von US-Adressen und Telefonen nach. Es kann leicht erweitert werden, um allgemeinere Situationen zu berücksichtigen.
  • In R hat das Zeichen "\" eine besondere Bedeutung für Zeichenvariablen, daher müssen wir es umgehen.
  • Um das Definieren regulärer Ausdrücke zu vereinfachen, empfiehlt es sich, die folgende Webseite zu verwenden: regex101.com , damit Sie mit einem bestimmten Beispiel damit spielen können, bis Sie das erwartete Ergebnis für alle möglichen Kombinationen erhalten.

Die Idee ist, jedes Linienfeld anhand zuvor definierter Muster zu identifizieren. Das Zustandsmuster definiert die folgenden Entitäten (Klassen), die zusammenarbeiten, um das spezifische Verhalten zu steuern (Das Zustandsmuster ist ein Verhaltensmuster):

GOF-Zustandsmuster

Beschreiben wir jedes Element unter Berücksichtigung des Kontexts unseres Problems:

  • Context : Speichert die Kontextinformationen des Parsing-Prozesses, dh den aktuellen Status und behandelt den gesamten State Machine-Prozess. Für jeden Status wird eine Aktion ausgeführt ( handle() ), die jedoch vom Kontext abhängig von der für einen bestimmten Status ( handle() von State Klasse) definierten Aktionsmethode delegiert wird. Es definiert die Schnittstelle, die für Kunden von Interesse ist. Unsere Context Klasse kann folgendermaßen definiert werden:
    • Attribute: state
    • Methoden: handle() , ...
  • State : Die abstrakte Klasse, die einen beliebigen Status der State Machine darstellt. Sie definiert eine Schnittstelle zum Einkapseln des Verhaltens, das einem bestimmten Zustand des Kontextes zugeordnet ist. Es kann wie folgt definiert werden:
    • Attribute: name, pattern
    • Methoden: doAction() , isState (mit pattern - Attribute überprüfen , ob das Eingabeargument zu diesem Zustand Mustern gehört oder nicht), ...
  • Concrete States (Statusunterklassen): Jede Unterklasse der Klasse State , die ein Verhalten implementiert, das einem Zustand des Context . Unsere Unterklassen sind: InitState , NameState , AddressState , PhoneState . Solche Klassen implementieren nur die generische Methode unter Verwendung der spezifischen Logik für solche Zustände. Es sind keine zusätzlichen Attribute erforderlich.

Hinweis: Es ist eine Frage der Präferenz, wie die Methode benannt wird, die die Aktion ausführt, handle() , doAction() oder goNext() . Der Methodenname doAction() kann für beide Klassen ( State oder Context ) derselbe sein, den wir als handle() in der Context Klasse benannt haben, um Verwirrung zu vermeiden, wenn zwei generische Methoden mit denselben Eingabeargumenten, jedoch unterschiedlichen Klassen definiert werden.

PERSONENKLASSE

Mit der S4-Syntax können wir eine Person-Klasse folgendermaßen definieren:

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

Es ist eine gute Empfehlung, die Klassenattribute zu initialisieren. Die setClass Dokumentation schlägt vor, eine generische Methode zu verwenden, die als "initialize" , anstatt veraltete Attribute wie prototype, representation .

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

Da die Methode initialize bereits eine generische Standardmethode Paket ist methods , müssen wir die ursprüngliche Argument Definition respektieren. Wir können die Eingabe an der Eingabeaufforderung R überprüfen:

> initialize

Es gibt die gesamte Funktionsdefinition zurück. Sie können oben sehen, wer die Funktion wie folgt definiert:

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

Wenn wir setMethod verwenden, setMethod wir daher genau dieselbe Syntax ( .Object ) verwenden.

Eine andere existierende generische Methode ist show , sie entspricht toString() Methode von Java und es ist eine gute Idee, eine spezifische Implementierung für die Klassendomäne zu haben:

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

Hinweis : Wir verwenden dieselbe Konvention wie in der Standard-Java-Implementierung toString() .

Angenommen, wir möchten die geparsten Informationen (eine Liste von Person ) in einem Dataset speichern. Dann sollten wir zuerst eine Liste von Objekten in etwas konvertieren können, das von R transformiert werden kann (z. B. das Objekt als Liste zwingen). Wir können die folgende zusätzliche Methode definieren (für weitere Details siehe den Beitrag )

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 bietet keine Zuckersyntax für OO, da die Sprache ursprünglich so konzipiert wurde, dass sie für Statistiker wertvolle Funktionen bereitstellt. Daher erfordert jede Benutzermethode zwei Teile: 1) den Definitionsteil (über setGeneric ) und 2) den Implementierungsteil (über setMethod ). Wie im obigen Beispiel.

STAATSKLASSE

Nach der S4-Syntax definieren wir die abstrakte State Klasse.

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"))

Jede Unterklasse von State hat einen name und ein pattern , aber auch eine Möglichkeit, um zu ermitteln, ob eine bestimmte Eingabe zu diesem Status gehört ( isState() Methode), und die entsprechenden Aktionen für diesen Status zu doAction() Methode).

Um den Prozess zu verstehen, definieren wir die Übergangsmatrix für jeden Status basierend auf der erhaltenen Eingabe:

Eingang / aktueller Status Drin Name Adresse Telefon
Name Name
Adresse Adresse
Telefon Telefon Telefon
Ende Ende

Hinweis: Die Zelle [row, col]=[i,j] repräsentiert den Zielzustand für den aktuellen Zustand j , wenn sie die Eingabe i empfängt.

Dies bedeutet, dass unter dem Status Name zwei Eingaben empfangen werden können: eine Adresse oder eine Telefonnummer. Eine andere Möglichkeit, die Transaktionstabelle darzustellen, ist das folgende UML State Machine- Diagramm:

Darstellung des Zustandsmaschinendiagramms

Lassen Sie uns jeden einzelnen Zustand als einen Unterzustand der Klasse State implementieren

STAATLICHE UNTERKLASSEN

Init State :

Der Anfangszustand wird über folgende Klasse implementiert:

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

In R bezeichnet eine Klasse eine Unterklasse einer anderen Klasse, die das Attribut contains und den Klassennamen der übergeordneten Klasse angibt.

Da die Unterklassen nur die generischen Methoden implementieren, ohne zusätzliche Attribute hinzuzufügen, rufen Sie die show Methode einfach die entsprechende Methode von der oberen Klasse auf (über die Methode callNextMethod() ).

Dem Anfangszustand ist kein Muster zugeordnet, er stellt lediglich den Anfang des Prozesses dar. Dann initialisieren wir die Klasse mit einem NA Wert.

Jetzt können Sie die generischen Methoden aus der State Klasse implementieren:

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

Für diesen bestimmten Zustand (ohne pattern ) wird die Idee, dass der Parsing-Prozess mit dem ersten Feld nur initialisiert wird, ein name , andernfalls ein Fehler.

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

Die Methode doAction stellt den Übergang bereit und aktualisiert den Kontext mit den extrahierten Informationen. Hier greifen wir über den @-operator . Stattdessen können wir get/set Methoden definieren, um diesen Prozess zu kapseln (wie es in den OO-Best Practices vorgeschrieben ist: encapsulation). Dies würde jedoch vier weitere Methoden pro get-set hinzufügen get-set ohne für dieses Beispiel einen Mehrwert zu schaffen.

Es ist eine gute Empfehlung in allen doAction Implementierungen, einen Schutz hinzuzufügen, wenn das Eingabeargument nicht richtig identifiziert wird.

Name Staat

Hier ist die Definition dieser Klassendefinition:

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

Wir verwenden die Funktion grepl um zu überprüfen, grepl die Eingabe zu einem gegebenen Muster gehört.

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

Jetzt definieren wir die Aktion, die für einen bestimmten Zustand ausgeführt werden soll:

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

Hier betrachten wir mögliche Übergänge: einen für den Adressstatus und den anderen für den Telefonstatus. In allen Fällen aktualisieren wir die Kontextinformationen:

  • Die person : address oder phone mit dem Eingabeargument.
  • Der state des Prozesses

Um den Zustand zu identifizieren, rufen Sie die Methode auf: isState() für einen bestimmten Zustand. Wir erstellen standardmäßige Zustände ( addressState, phoneState ) und fragen nach einer bestimmten Validierung.

Die Logik für die Implementierung der anderen Unterklassen (eine pro Status) ist sehr ähnlich.

Adressstatus

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

Telefonzustand

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

Hier fügen wir die Personeninformationen in die Liste der persons des context .

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

KONTEXTKLASSE

Nun erklären Context Implementierung der Context Klasse. Wir können es unter Berücksichtigung der folgenden Attribute definieren:

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

Woher

  • state : Der aktuelle Status des Prozesses
  • person : Die aktuelle Person, die die Informationen darstellt, die wir bereits aus der aktuellen Zeile analysiert haben.
  • persons : Die Liste der analysierten Personen.

Anmerkung : Optional können wir einen name hinzufügen, um den Kontext anhand des name zu identifizieren, falls mit mehr als einem Parser-Typ gearbeitet wird.

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"))

Mit solchen generischen Methoden kontrollieren wir das gesamte Verhalten des Analyseprozesses:

  • handle() : doAction() die bestimmte doAction() -Methode des aktuellen state .
  • addPerson : Sobald wir den addPerson erreicht haben, müssen wir eine person zur Liste der persons hinzufügen, die wir analysiert haben.
  • parseLine() : Analysiert eine einzelne Zeile
  • parseLines() : Analysiert mehrere Zeilen (ein parseLines() )
  • as.df() : Extrahieren Sie die Informationen aus der persons in ein as.df() .

Lassen Sie uns nun mit den entsprechenden Implementierungen fortfahren:

handle() -Methode, delegiert die doAction() -Methode aus dem aktuellen state des context :

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

Zuerst teilen wir die ursprüngliche Zeile in einem Array mit dem Trennzeichen auf, um jedes Element über die R-Funktion strsplit() zu identifizieren, und strsplit() dann jedes Element als Eingabewert für einen bestimmten Zustand. Die Methode handle() gibt den context mit den aktualisierten Informationen ( state , person , persons ) erneut zurück.

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

Becuase R kopiert das Eingabeargument. Wir müssen den Kontext ( obj ) zurückgeben:

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

Das Attribut persons ist eine Liste von Exemplaren der Klasse S4 Person . Dieses Etwas kann nicht zu einem Standardtyp erzwungen werden, da R nicht weiß, dass es eine Instanz einer benutzerdefinierten Klasse behandelt. Die Lösung besteht darin, eine Person mithilfe der zuvor definierten as.list Methode in eine Liste zu konvertieren. Dann können wir diese Funktion auf jedes Element der Liste gelten persons , über die lapply() Funktion. data.frame dann beim nächsten Aufruf von lappy() Funktion data.frame , um jedes Element der persons.list in einen data.frame zu konvertieren. Schließlich wird die Funktion rbind() aufgerufen, um jedes Element, das als neue Zeile des generierten rbind() konvertiert wurde, rbind() hierzu finden Sie in diesem Beitrag ).

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

ALLES ZUSAMMENSTELLEN

Lassen Sie uns schließlich die gesamte Lösung testen. Definieren Sie die Zeilen, in denen analysiert werden soll, wo für die zweite Zeile die Adressinformationen fehlen.

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"
)

Jetzt initialisieren wir den context und parsen die Zeilen:

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

Holen Sie sich schließlich den entsprechenden Datensatz und drucken Sie ihn aus:

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

Testen wir jetzt die show Methoden:

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

Und für einige Unterzustände:

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

Testen Sie as.list() die as.list() -Methode:

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

$address
[1] "25 NE 25TH"

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

> 

FAZIT

Dieses Beispiel zeigt, wie das Zustandsmuster implementiert wird, wobei einer der verfügbaren Mechanismen von R für die Verwendung des OO-Paradigmas verwendet wird. Dennoch ist die R OO-Lösung nicht benutzerfreundlich und unterscheidet sich so sehr von anderen OOP-Sprachen. Sie müssen Ihre Denkweise ändern, da die Syntax völlig anders ist und mehr an das Paradigma der funktionalen Programmierung erinnert. Zum Beispiel anstelle von object.setID("A1") wie in Java / C #, müssen Sie für R die Methode folgendermaßen aufrufen: setID(object, "A1") . Daher müssen Sie das Objekt immer als Eingabeargument angeben, um den Kontext der Funktion bereitzustellen. Auf dieselbe Weise gibt es kein spezielles Attribut this Klasse und entweder ein "." Notation für den Zugriff auf Methoden oder Attribute der angegebenen Klasse. Es ist eine weitere Fehleraufforderung, da das Verweisen auf eine Klasse oder Methoden über den Attributwert ( "Person" , "isState" usw.) erfolgt.

Die obige S4-Klassenlösung erfordert viel mehr Codezeilen als herkömmliche Java / C # -Sprachen, um einfache Aufgaben auszuführen. Wie auch immer, das State Pattern ist eine gute und generische Lösung für solche Probleme. Es vereinfacht den Prozess, der die Logik in einen bestimmten Zustand delegiert. Anstelle eines großen if-else Blocks zum Steuern aller Situationen haben wir in jeder State Unterklassenimplementierung kleinere if-else Blöcke zur Implementierung der in jedem Staat auszuführenden Aktion.

Anhang : Hier können Sie das gesamte Skript herunterladen.

Jeder Vorschlag ist willkommen.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow