Является ли композиция с Funcs предпочтительнее наследования, чтобы изменить поведение в одной функции? - PullRequest
1 голос
/ 01 февраля 2012

Я работаю над созданием консоли 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?Или есть общий шаблон проектирования, который используется в подобных ситуациях, который я, возможно, упускаю?

1 Ответ

4 голосов
/ 01 февраля 2012

Передача Func в качестве фабричного метода мне кажется разумной - но вы можете перегрузить ваш конструктор, чтобы принять более простую форму:

public class IronPythonConsoleHost : ConsoleHost
{
    // Full version
    public IronPythonConsoleHost(
       Func<ScriptEngine, CommandLine, ConsoleOptions, IConsole> consoleSelector)
    {
        _consoleSelector = consoleSelector;
    }

    public IronPythonConsoleHost(Func<IConsole> consoleSelector)
        this((engine, commandLine, options) => consoleSelector())
    {
    }

    ...
    public override IConsole CreateConsole(ScriptEngine engine,
        CommandLine commandLine, ConsoleOptions options)
    {
        return _consoleSelector(engine, commandLine, options);
    }
    ...
}

Таким образом, вы можете написать:

_console = new IronPythonConsoleHost(() => new XnaConsole());
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...