【问题标题】:How could I refactor this into more manageable code?我怎样才能将其重构为更易于管理的代码?
【发布时间】:2010-04-16 09:02:28
【问题描述】:

这段代码是否足够复杂,值得更高层次的抽象?

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

【问题讨论】:

  • 既然已经有这么多好的替代方案,为什么还要编写自己的 JSON 解析器?
  • @Hightechrider - 当我可以在商店买到一台非常好的电脑时,我为什么要制造我的电脑?因为这是一种有趣且具有教育意义的体验。

标签: c# json refactoring parsing


【解决方案1】:

不仔细看,因为你是基于状态解析的,你可以使用state pattern来分解它并根据状态在一个单独的类中解析每个位吗?

这样的事情可能是一个开始,虽然这只是伪代码......

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

【讨论】:

  • 在这种情况下我更喜欢委托,而不是单方法接口。
  • @Dykam:也许可以,但是通过接口和类,您可以将代码分离到逻辑上不同的单独文件中。并且OP可能需要其他方法,我只是举一个例子来说明我的意思。我想在实际实现中可能需要改变......
  • 我仍然希望得到其他意见,但状态模式当然是一个优雅的选择。
  • 在委托的情况下,您也可以以相同的方式实现它,但您传递的是方法而不是类。我提倡这种方式,因为它是相同的,但更灵活。虽然当你想要 2 种方法时,接口是唯一的方法。
  • 感谢您花时间写出答案。
【解决方案2】:

在这种情况下,“概念设计”是生产规则。如果您要自己设计 json,您会按照“一对是键后跟冒号后跟值”的方式思考,还是按照“在这种情况下,冒号将执行 'a' 和在“B”的情况下做“b”,在“C”的情况下做“c””?看http://www.json.org/。您将看到以生产规则表述的“概念设计”。

由于代码的“结构设计”没有“概念设计”的形式,再多的重构也无济于事。对“概念设计”进行少量更改,将导致难以编码且难以测试的代码更改。您需要根据“概念设计”重写代码。

// 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

【讨论】:

  • 请解释一下“在这种情况下,‘设计’是生产规则。”是什么意思。
  • 我不会说我的代码缺乏设计。它是一个有限状态机。它可能不遵循 OO 原则,但如果你遵循这些状态,你会发现它实际上非常好。不过我确实喜欢你的想法。
  • 很抱歉,您觉得我认为您的代码没有代码设计或代码设计不佳。你的代码写得很好。我想我是说结构设计应该遵循概念设计。
  • 我想我们现在在同一个页面上。我肯定会再次检查设计。
猜你喜欢
  • 1970-01-01
  • 2021-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-15
  • 2016-05-01
  • 2017-11-17
  • 1970-01-01
相关资源
最近更新 更多