I once faced a similar problem. While my solution certainly is not optimal, it did the trick.
I used an additional layer between the actual compiling and the concurrently used Compiler
-class. This class would be called Compilation
in your example.
/*
This class exists once for multiple (simultaneous) inputs
*/
public class Compiler{
CompilationParameters compilationParameters;
public Compiler(CompilationParameters params){
...
}
public CompilationResult compile(CompilationInput input){ // Of course you can simplify this to a string
Compilation compilation = new Compilation(compilationParameters, input);
compilation.compile();
return compilation.getResult();
}
}
/*
This class exists exactly once per input
*/
public class Compilation{
CompilationState state;
public Compiler(CompilationParameters params, CompilationInput input){
...
}
public void compile(){
...
}
public CompilationResult getResult(){
...
}
}
Now you could argue, that this violates the Open-Closed-Principle. And you'd be right. This solution can be extended for different types of compilations using a factory-pattern for the creation of the Compilation
-object. Imagine the following:
public abstract class Compilation{...};
public class CompiletimeOptimizedCompilation extends Compilation{...};
public class RuntimeOptimizedCompilation extends Compilation{...};
public class CompilationFactory{
public static Compilation createCompilation(CompilationType type, CompilationParameters params, CompilationInput input)
{
switch(CompilationType)
{
case eCompileTimeOptimized:
return new CompileTimeOptimizedCompilation(params, input);
case eRuntimeOptimized:
return new RuntimeOptimizedCompilation(params, input);
case ...
}
}
}
In this case Compilation
might also be an interface instead of an abstract class. To decide that I'd probably need more details about the implementations. And of course the Compiler
-class would boil down to:
public class Compiler{
CompilationParameters compilationParameters;
public Compiler(CompilationType type, CompilationParameters params){ // This type CAN be part of the parameters
...
}
public CompilationResult compile(CompilationInput input){ // Of course you can simplify this to a string
Compilation compilation = CompilationFactory.createCompilation(type, compilationParameters, input);
compilation.compile();
return compilation.getResult();
}
}
Of course the same works for your Parser
-class, but I thought it would be a bad example because the noun of "to parse" is "the parse".
If you have any further questions or remarks, don't hesitate asking.
PS: Please excuse any spelling mistakes. I'm not a native.
each module should have only one reason to change
-- this is in terms of software evolution (changes in source code, not changes in state).State
? If a state is bound to an input, why not receiving it as a parameter (inparse
orcompile
) ?