理解开放封闭原则

时间:2011-03-24 08:28:12

标签: c# parsing open-closed-principle

当我遇到以下代码时,我重构 一个简单的脚本文件解析器的旧代码:

StringReader reader = new StringReader(scriptTextToProcess);
StringBuilder scope = new StringBuilder();
string line = reader.ReadLine();
while (line != null)
{
    switch (line[0])
    {
        case '$':
            // Process the entire "line" as a variable, 
            // i.e. add it to a collection of KeyValuePair.
            AddToVariables(line);
            break;
        case '!':
            // Depending of what comes after the '!' character, 
            // process the entire "scope" and/or the command in "line".
            if (line == "!execute")
                ExecuteScope(scope);
            else if (line.StartsWith("!custom_command"))
                RunCustomCommand(line, scope);
            else if (line == "!single_line_directive")
                ProcessDirective(line);

            scope = new StringBuilder();
            break;

        default:
            // No processing directive, i.e. add the "line" 
            // to the current scope.
            scope.Append(line);
            break;
    }

    line = reader.ReadLine();
}

这个简单的脚本处理器在我看来似乎是通过应用“开放封闭原则”进行重构的一个很好的候选者。以$开头的行可能永远不会以不同的方式处理。但是,如果需要添加以!开头的新指令呢?或者需要新的处理标识符(例如新的开关案例)?

问题是,我无法弄清楚如何在不破坏OCP的情况下轻松正确地添加更多指令和处理器。使用! 和/或 scope的{​​{1}}个案例使其有点棘手,line - 案例也是如此。

有什么建议吗?

1 个答案:

答案 0 :(得分:23)

使用Dictionary<Char, YourDelegate>指定角色的处理方式。如果字典中不存在字符键,请调用DefaultHandler

添加Add(char key, YourDelegate handler)方法,允许任何人处理特定字符。

更新

最好使用接口:

/// <summary>
/// Let anyone implement this interface.
/// </summary>
public interface IMyHandler
{
    void Process(IProcessContext context, string line);
}

/// <summary>
/// Context information
/// </summary>
public interface IProcessContext
{
}


// Actual parser
public class Parser
{
    private Dictionary<char, IMyHandler> _handlers = new Dictionary<char, IMyHandler>();
    private IMyHandler _defaultHandler;

    public void Add(char controlCharacter, IMyHandler handler)
    {
        _handlers.Add(controlCharacter, handler);
    }

    private void Parse(TextReader reader)
    {
        StringBuilder scope = new StringBuilder();
        IProcessContext context = null; // create your context here.

        string line = reader.ReadLine();
        while (line != null)
        {
            IMyHandler handler = null;
            if (!_handlers.TryGetValue(line[0], out handler))
                handler = _defaultHandler;

            handler.Process(context, line);


            line = reader.ReadLine();
        }
    }
}

请注意,我会传递TextReader。它提供了更大的灵活性,因为源可以是从简单字符串到复杂流的任何内容。

更新2

我也会以类似的方式解析!处理。即创建一个处理IMyHandler的类:

public interface ICommandHandler
{
    void Handle(ICommandContext context, string commandName, string[] arguments);
}

public class CommandService : IMyHandler
{
    public void Add(string commandName, ICommandHandler handler) 
    {
    }

    public void Handle(IProcessContext context, string line)
    {
       // first word on the line is the command, all other words are arguments.
       // split the string properly

       // then find the corrext command handler and invoke it.
       // take the result and add it to the `IProcessContext`
    }
}

这为处理实际协议和添加更多命令提供了更大的灵活性。您无需更改任何内容即可添加更多功能。因此,对于Open / Closed和其他一些SOLID原则,解决方案是可行的。