I am working on creating an IronPython console similar to the IronPythonConsole project provided with the IronPython source code. However, I want to make the ConsoleHost more extensible than the sample provided, such that it could be extended to run an IronPython interpreter in XNA, or WPF etc. As such, I have created a class IronPythonConsoleHost that extends ConsoleHost (from Microsoft.Scripting.Hosting.Shell), modelled on the PythonConsoleHost source.
public class IronPythonConsoleHost : ConsoleHost
{
...
public override IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options)
{
return new IronPythonConsole();
}
...
}
// run in a ConsoleApplication
static void Main(string[] args)
{
new IronPythonConsoleHost().Run(args);
}
This creates a situation where IronPythonConsoleHost can be extended in other applications. For instance, a console could be integrated into XNA as follows (assuming appropriate supporting classes):
public class XnaConsoleHost : IronPythonConsoleHost
{
public override IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options)
{
return new XnaConsole();
}
}
However, this creates a situation where I am inheriting IronPythonConsoleHost simply to override a single method – no other functionality of the class needs to be changed. Furthermore, XnaConsoleHost is not being used in any situation that expects an IronPythonConsoleHost (or indeed a ConsoleHost), and this would seem to go against the Liskov substitution principle, as there is no usage scenario where substitutability is required or appropriate.
It seems it would be more appropriate to design IronPythonConsoleHost such that it can be provided with a means of selecting what IConsole to create (i.e. using composition instead of inheritance):
public class IronPythonConsoleHost : ConsoleHost
{
public IronPythonConsoleHost(Func<ScriptEngine, CommandLine, ConsoleOptions, IConsole> consoleSelector)
{
_consoleSelector = consoleSelector;
}
...
public override IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options)
{
return _consoleSelector(engine, commandLine, options);
}
...
}
This would allow the XNA game to simply construct an IronPythonConsoleHost with the appropriate selector function:
_console = new IronPythonConsoleHost((eng, cl, co) => new XnaConsole());
This approach works, but passing Funcs seems somewhat clunky. This definitely seems like a situation where inheritance is a much less robust solution, but I don’t think composition via awkward Funcs is necessarily better (especially if selectors are required for other areas – as they probably will be).
Is composition a good approach for this situation, and if so, are Funcs appropriate? Or is there a common design pattern that is used in similar situations that I am perhaps missing?
Passing a
Funcas a factory method seems reasonable to me – but you may want to overload your constructor to accept a simpler form:So you could then write: