Я работаю над созданием консоли IronPython, похожей на проект IronPythonConsole, с исходным кодом IronPython .Однако я хочу сделать ConsoleHost
более расширяемым, чем предоставленный образец, чтобы его можно было расширить для запуска интерпретатора IronPython в XNA или WPF и т. Д. Поэтому я создал класс IronPythonConsoleHost
, который расширяет * 1005.* (из Microsoft.Scripting.Hosting.Shell
), смоделировано на источнике PythonConsoleHost
.
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);
}
Это создает ситуацию, в которой IronPythonConsoleHost
может быть расширен в других приложениях.Например, консоль может быть интегрирована в XNA следующим образом (при условии наличия соответствующих вспомогательных классов):
public class XnaConsoleHost : IronPythonConsoleHost
{
public override IConsole CreateConsole(ScriptEngine engine, CommandLine commandLine, ConsoleOptions options)
{
return new XnaConsole();
}
}
Однако это создает ситуацию, когда я наследую IronPythonConsoleHost
просто для переопределения одного метода - никакого другогофункциональность класса должна быть изменена.Кроме того, XnaConsoleHost
не используется ни в одной ситуации, которая ожидает IronPythonConsoleHost
(или даже ConsoleHost
), и это, по-видимому, идет вразрез с принципом подстановки Лискова , поскольку здесь нет использованияСценарий, в котором заменяемость требуется или уместна.
Кажется, было бы более уместно спроектировать IronPythonConsoleHost
так, чтобы он мог быть снабжен средством выбора того, что IConsole
создать (т. е. с использованием композиции вместо наследования):
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);
}
...
}
Это позволило бы игре XNA просто создать IronPythonConsoleHost
с соответствующей функцией выбора:
_console = new IronPythonConsoleHost((eng, cl, co) => new XnaConsole());
Этот подход работает, но кажется, что пропуск Func
sнесколько неуклюжийЭто определенно похоже на ситуацию, когда наследование является гораздо менее надежным решением, но я не думаю, что составление через неловкие Func
s обязательно лучше (особенно если селекторы требуются для других областей - как они, вероятно, будут).
Является ли композиция хорошим подходом для этой ситуации, и если да, то подходят ли Func
?Или есть общий шаблон проектирования, который используется в подобных ситуациях, который я, возможно, упускаю?