Общий базовый класс, переопределяющий неуниверсальный шаблон функции базового класса?(.СЕТЬ) - PullRequest
0 голосов
/ 17 октября 2018

Мне интересно, есть ли у кого-нибудь хорошее предложение / шаблон для решения следующей проблемы дизайна.У меня есть иерархия командных классов.На самом абстрактном уровне у меня есть интерфейс ICommand.Результатом выполнения функции RunCommand () ICommand является объект.Разные команды будут иметь разные типы результатов, так что это подходящая абстракция.

Построение иерархии немного больше, становится желательным использование обобщений.Я создаю интерфейс Generic.ICommand (Of TResult).

Существует некоторый распространенный код рабочей панели, такой как Run, TryRun, BeginRun, BeginTryRun и т. Д., Который я хочу для всех команд - поэтому я создаю класс BaseCommand, который предоставляет все это и реализует неуниверсальный интерфейс ICommand.Однако BaseCommand не знает, как на самом деле выполнить что-либо, поэтому все эти команды в конечном итоге вызывают защищенную абстрактную функцию с именем InternalRunCommand.Все это прекрасно работает.

Теперь я хочу создать универсальную версию этого класса: BaseCommand (Of T).Он наследуется от BaseCommand, а также реализует общий интерфейс ICommand (Of T).Это работает, но теперь существует несоответствие: InternalRunCommand.

В неуниверсальной версии InternalRunCommand возвращает объект.В моем универсальном классе BaseCommand (Of T) я хотел бы перегрузить его универсальной версией, которая возвращает результат как тип T. К сожалению, компиляторы VB.NET/C# не позволяют вам перегрузить метод, гдеЕдинственное отличие - это тип возвращаемого значения.

Поскольку это защищенная функция, в конечном итоге она не имеет большого значения для общего API, но, тем не менее, меня раздражает, что у меня нет эстетически приятного решения этой проблемы.немного архитектуры.

В настоящее время я переопределил неуниверсальный InternalRunCommand в классе BaseCommand (Of T), чтобы он вызывал новую защищенную абстрактную функцию OnRunCommand, которая принимает те же параметры, но возвращает результаттипа T. InternalRunCommand также был объявлен как NonOverridable.Это, возможно, самое близкое, что я могу получить - но хотел посмотреть, есть ли какие-нибудь лучшие идеи там?:)

РЕДАКТИРОВАТЬ: я включил упрощенную копию кода в соответствии с просьбой, чтобы вы могли лучше представить проблему:

Public Interface ICommand
    Property Name as String
    Property Description As String
    Property ResultType as Type
    Function RunCommand(target as Device) As Object
    Function TryRunCommand(target as Device, Byref result as Object) AS Boolean
    Function BeginRunCommand(target as Device) as Task(Of Object)
    Function BeginTryRunCommand(target as Device) As Task(of Boolean)
End Interface

Namespace Generic
Public Interface ICommand(Of TResult)
    Function RunCommand(target as Device) as T
    Function BeginRunCommand(target as Device) As Task(Of T)
End Interface
End Namespace

Public MustInherit Class BaseCommand
    Implements ICommand

    Public Function RunCommand(target as Device) As Object Implements ICommand.RunCommand
        Return InternalRunCommand(device)
    End Function

    Public Function BeginRunCommand(target as Device) As Task(of Object) Implements ICommand.BeginRunCommand
        Return Task(Of Object).Factory.StartNew( Function() InternalRunCommand(target))
    End Function

    ' Other boiler plate code goes here'

    Protected MustOverride Function InternalRunCommand(target as Device) As Object

End Class

Namespace Generic
Public Class BaseCommand(Of TResult)
    Inherits BaseCommand
    Implements ICommand(Of TResult)

    Public Function BeginRunCommand(target as Device) As Task(of TResult) Implements ICommand(Of TResult).BeginRunCommand
        Return Task(Of TResult).Factory.StartNew( Function() OnRunCommand(target))
    End Function

    Protected NotOverridable Overrides Function InternalRunCommand(target as Device) As Object
        ' Re-route to the generic version'
        Return OnRunCommand(device)
    End Function

    Protected MustOverride Function OnRunCommand(target as Device) As T
End Class

1 Ответ

0 голосов
/ 19 октября 2018

Мне кажется, я нашел хороший шаблон, который позволяет вам переопределить неуниверсальную версию таких функций с помощью универсальных и без каких-либо грязных функций-оболочек, как у меня в ОП.

I 'мы заменили защищенную абстрактную функцию InnerRunCommand защищенным свойством ReadOnly с тем же именем.Тип этого свойства - Func (Of ICommand, Device, Object).Я изменил конструктор класса BaseCommand для принятия такого объекта Func.

В классе Generic.BaseCommand (Of T) я могу скрыть InnerRunCommand с помощью аналогичного свойства типа Func (Of ICommand, Device,Т).Конструктор для Generic.BaseCommand (Of T) аналогичным образом принимает такой объект Func и передает этот объект обратно в неуниверсальный конструктор BaseCommand без проблем:)

Я нахожусь в процессе модификации моегоархитектура для поддержки этого нового шаблона, и даст вам всем знать, если я столкнусь с какими-либо проблемами.Я приветствую любую критику этого подхода и приветствую альтернативные ответы:)

РЕДАКТИРОВАТЬ: я построил простой пример предложенного шаблона ниже.Я написал это на C # вместо VB.NET.В приведении объектов FUNC в C # немного больше работы (VB.NET справится с этим за кулисами).Однако на обоих языках использование этих анонимных функций делает API чистым и расширяемым.

public interface ICommand
{
    object Execute();
    Boolean TryExecute(out object result);
    Task<object> BeginExecute();        
}

namespace Generic
{
    public interface ICommand<TResult> : ICommand
    {
        new TResult Execute();
        Boolean TryExecute(out TResult result);
        new Task<TResult> BeginExecute();
    }
}

public class Command : ICommand
{
    private Func<ICommand, object> _execFunc = null;
    protected Func<ICommand, object> ExecFunc { get { return _execFunc; } }

    public Task<object> BeginExecute()
    {
        return Task<object>.Factory.StartNew(() => _execFunc(this) );
    }

    public object Execute()
    {
        return _execFunc(this);
    }

    public bool TryExecute(out object result)
    {
        try
        {
            result = _execFunc(this);
            return true;
        }
        catch(Exception ex)
        {
            result = null;
            return false;
        }
    }

    public Command (Func<ICommand, object> execFunc)
    {
        if (execFunc == null) throw new ArgumentNullException("execFunc");
        _execFunc = execFunc;
    }
}

namespace Generic
{
    public class Command<TResult> : Command, ICommand<TResult> where TResult : class            
    {
        new protected Func<ICommand<TResult>, TResult> ExecFunc => (ICommand<TResult> cmd) => (TResult)base.ExecFunc(cmd);

        public bool TryExecute(out TResult result)
        {
            try
            {
                result = ExecFunc(this);
                return true;
            }
            catch(Exception ex)
            {
                result = null;
                return false;
            }
        }

        Task<TResult> ICommand<TResult>.BeginExecute()
        {
            return Task<TResult>.Factory.StartNew(() => ExecFunc(this) );
        }

        TResult ICommand<TResult>.Execute()
        {
            return ExecFunc(this);
        }

        public Command(Func<ICommand<TResult>, TResult> execFunc) : base((ICommand c) => (object)execFunc((ICommand<TResult>)c))
        {
        }
    }
}

public class ConcatCommand : Generic.Command<string> 
{
    private IEnumerable<string> _inputs;
    public IEnumerable<String> Inputs => _inputs;

    public ConcatCommand(IEnumerable<String> inputs) : base( (Generic.ICommand<string> c) => (string)String.Concat(((ConcatCommand)c).Inputs) )
    {
        if (inputs == null) throw new ArgumentNullException("inputs");
        _inputs = inputs;
    }

}

class Program
{
    static void Main(string[] args)
    {
        string[] inputs = { "This", " is ", " a ", " very ", " fine ", " wine!" };
        ICommand c = new ConcatCommand(inputs );
        string results = (string)c.Execute();
        Console.WriteLine(results);
        Console.ReadLine();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...