2010-04-16 15 views
9

Questo codice è abbastanza complesso da meritare un livello più alto di astrazione?Come posso refactoring questo in codice più gestibile?

public static JsonStructure Parse(string jsonText) 
{ 
    var result = default(JsonStructure); 
    var structureStack = new Stack<JsonStructure>(); 
    var keyStack = new Stack<string>(); 
    var current = default(JsonStructure); 
    var currentState = ParserState.Begin; 
    var key = default(string); 
    var value = default(object); 

    foreach (var token in Lexer.Tokenize(jsonText)) 
    { 
     switch (currentState) 
     { 
      case ParserState.Begin: 
       switch (token.Type) 
       { 
        case TokenType.BeginObject: 
         currentState = ParserState.Name; 
         current = result = new JsonObject(); 
         break; 
        case TokenType.BeginArray: 
         currentState = ParserState.Value; 
         current = result = new JsonArray(); 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.Name: 
       switch (token.Type) 
       { 
        case TokenType.String: 
         currentState = ParserState.NameSeparator; 
         key = (string)token.Value; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.NameSeparator: 
       switch (token.Type) 
       { 
        case TokenType.NameSeparator: 
         currentState = ParserState.Value; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.Value: 
       switch (token.Type) 
       { 
        case TokenType.Number: 
        case TokenType.String: 
        case TokenType.True: 
        case TokenType.False: 
        case TokenType.Null: 
         currentState = ParserState.ValueSeparator; 
         value = token.Value; 
         break; 
        case TokenType.BeginObject: 
         structureStack.Push(current); 
         keyStack.Push(key); 
         currentState = ParserState.Name; 
         current = new JsonObject(); 
         break; 
        case TokenType.BeginArray: 
         structureStack.Push(current); 
         currentState = ParserState.Value; 
         current = new JsonArray(); 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.ValueSeparator: 
       var jsonObject = (current as JsonObject); 
       var jsonArray = (current as JsonArray); 
       if (jsonObject != null) 
       { 
        jsonObject.Add(key, value); 
        currentState = ParserState.Name; 
       } 
       if (jsonArray != null) 
       { 
        jsonArray.Add(value); 
        currentState = ParserState.Value; 
       } 
       switch (token.Type) 
       { 
        case TokenType.EndObject: 
        case TokenType.EndArray: 
         currentState = ParserState.End; 
         break; 
        case TokenType.ValueSeparator: 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      case ParserState.End: 
       switch (token.Type) 
       { 
        case TokenType.EndObject: 
        case TokenType.EndArray: 
        case TokenType.ValueSeparator: 
         var previous = structureStack.Pop(); 
         var previousJsonObject = (previous as JsonObject); 
         var previousJsonArray = (previous as JsonArray); 
         if (previousJsonObject != null) 
         { 
          previousJsonObject.Add(keyStack.Pop(), current); 
          currentState = ParserState.Name; 
         } 
         if (previousJsonArray != null) 
         { 
          previousJsonArray.Add(current); 
          currentState = ParserState.Value; 
         } 
         if (token.Type != TokenType.ValueSeparator) 
         { 
          currentState = ParserState.End; 
         } 
         current = previous; 
         break; 
        default: 
         throw new JsonException(token, currentState); 
       } 
       break; 
      default: 
       break; 
     } 
    } 
    return result; 
} 
+1

Perché dovresti scrivere il tuo parser JSON quando ci sono già tante buone alternative là fuori? –

+3

@Hightechrider - Perché dovrei costruire il mio PC quando posso acquistarne uno perfettamente buono nel negozio? Perché è un'esperienza divertente ed educativa. – ChaosPandion

risposta

10

Senza guardare in dettaglio, come si sta parsing in base allo stato, è possibile utilizzare il state pattern per disgregare e analizzare ogni bit in una classe separata in base allo stato?

qualcosa come questo potrebbe essere un inizio, anche se questo è solo pseudo codice ...

public interface IParserState 
    { 
    IParserState ParseToken (IToken token); 
    } 

public class BeginState : IParserState 
    { 
    private readonly Stack<JsonStructure> m_structureStack; 
    private readonly Stack<String> m_keyStack; 

    public BeginState (Stack<JsonStructure> structureStack, Stack<String> keyStack) 
     { 
     m_structureStack = structureStack; 
     m_keyStack = keyStack; 
     } 

    public IParserState ParseToken(IToken token) 
     { 
     switch (token.Type) 
      { 
      case TokenType.OpenBrace: 
       return new ObjectKeyParserState(m_structureStack,m_keyStack); 
      case TokenType.OpenBracket: 
       return new ArrayValueParserState(m_structureStack, m_keyStack); 
      default: 
       throw new JsonException (token);  
      } 
     } 
    } 

public class ObjectKeyParserState : IParserState 
    { 
    private readonly Stack<JsonStructure> m_structureStack; 
    private readonly Stack<String> m_keyStack; 
    private readonly JsonObject m_current; 

    public ObjectKeyParserState (Stack<JsonStructure> structureStack, Stack<String> keyStack) 
     { 
     m_current = new JsonObject(); 
     } 

    public IParserState ParseToken (IToken token) 
     { 
     switch (token.Type) 
      { 
      case TokenType.StringLiteral: 
       key = (string)token.Value; 
       return new ColonSeperatorParserState(m_structureStack, m_keyStack, m_current,key); 
      default: 
       throw new JsonException(token); 
      } 
     } 
+1

Preferisco i delegati in questo caso, invece delle interfacce a metodo singolo. – Dykam

+0

@Dykam: forse con interfacce e classi è possibile separare il codice in file separati che sono logicamente diversi. e l'OP potrebbe aver bisogno di altri metodi, stavo solo dando un esempio per mostrare cosa intendevo. Immagino che le cose potrebbero dover cambiare nell'implementazione attuale ... –

+0

Sto ancora sperando di ottenere altre opinioni ma il modello di stato è ovviamente un'opzione elegante. – ChaosPandion

2

Il 'progetto concettuale' in questo caso è regole di produzione. Se dovessi progettare tu stesso te stesso, penseresti in termini di "Una coppia è una chiave seguita da due punti seguiti da un valore" o penseresti in termini come "I coloni faranno 'a' in questo caso 'A' e fai 'b' nel caso 'B' e fai 'c' nel caso 'C' "? Guarda http://www.json.org/. Vedrai il 'disegno concettuale' dichiarato in termini di regole di produzione.

Poiché la 'progettazione strutturale' del codice non ha la forma del 'progetto concettuale', nessuna quantità di refactoring sarà d'aiuto. Cambiando il "progetto concettuale" una piccola quantità, si otterrebbe un cambiamento di codice difficile da codificare e difficile da testare. È necessario riscrivere il codice in termini di "progettazione concettuale".

// object 
// "{" "}" 
// "{" members "}" 
private static JsonObject ProduceJsonObject(Tokens tokens) 
{ 
    var result = new JsonObject(); 

    tokens.Accept(TokenType.OpenBrace); 
    result.members = ProduceJsonMembers(tokens); 
    tokens.Accept(TokenType.CloseBrace); 

    return result; 
} 

// members 
// pair 
// pair { "," pair } 
private static JsonMembers ProduceJsonMembers(Tokens tokens) 
{ 
    var result = new JsonMembers(); 

    result.Add(ProduceJsonPair(tokens)); 
    while (tokens.LookAhead == TokenTag.Comma) 
    { 
     tokens.Accept(TokenType.Comma); 
     result.Add(ProduceJsonPair(tokens)); 
    } 

    return result; 
} 

//pair 
// string ":" value 
private static JsonPair ProduceJsonPair(Tokens tokens) 
{ 
    var result = new JsonPair(); 

    result.String = tokens.Accept(TokenType.ID); 
    tokens.Accept(TokenType.Colon); 
    result.Value = ProduceJsonValue(tokens); 

    return result; 
} 


// and so forth 
+0

Spiega cosa * "Il 'design' in questo caso è regole di produzione." * Significa. – ChaosPandion

+0

Non direi esattamente che il mio codice manca di design. È una macchina a stati finiti. Potrebbe non seguire i principi OO, ma se segui gli stati, scoprirai che in realtà è piuttosto buono. Comunque mi piace la tua idea. – ChaosPandion

+0

sì, bella idea. –

Problemi correlati