I was refactoring some old code of a simple script file parser when I came across the following code:
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();
}
This simple script processor seems to me like a good candidate for refactoring by applying the "open closed principle". The lines beginning with a $
will probably never be handled differently. But, what if new directives beginning with a !
needs to be added? Or new processing identifiers (e.g. new switch-cases) are needed?
The problem is, I could not figure out how to easily and correctly add more directives and processors without breaking OCP. The !
-case using scope
and/or line
makes it a bit tricky, as does the default
-case.
Any suggestions?
Use a
Dictionary<Char, YourDelegate>
to specify how a character should be handled. CallDefaultHandler
if the character key do not exist in the dictionary.Add a
Add(char key, YourDelegate handler)
method allowing anyone to handle a specific character.Update
It's better to work with interfaces:
Note that I pass in a
TextReader
instead. It gives much more flexibility since the source can be anything from a simple string to a complex stream.Update 2
I would also break up the
!
handling in a similar way. i.e. Create a class that handles IMyHandler:That gives more flexibility for both handling the actual protocol and add more commands. you do not have to change anything to add more functionality. The solution is therefore OK regarding Open/Closed and some other SOLID principles.