我想知道是否有人对解决以下设计问题有好的建议/模式。我有一个命令类的层次结构。在最抽象的层次上,我有一个ICommand接口。执行ICommand的RunCommand()函数的结果是一个对象。不同的命令将具有不同的结果类型,因此这是一个适当的抽象。

进一步构建继承关系,使用泛型就变得很必要。我创建一个Generic.ICommand(Of TResult)接口。

我希望所有命令都有一些通用的样板代码,例如Run,TryRun,BeginRun,BeginTryRun等-因此,我创建了一个BaseCommand类来提供所有这些功能,并实现了非通用ICommand接口。但是,BaseCommand不知道如何实际执行任何操作,因此所有这些命令最终都会调用一个受保护的抽象函数,称为InternalRunCommand。这一切都很漂亮。

现在,我想创建该类的通用版本:BaseCommand(Of T)。它继承自BaseCommand,还实现了通用的ICommand(Of T)接口。这行得通,但是现在存在差异:InternalRunCommand。

在非通用版本中,InternalRunCommand返回一个Object。在我的通用BaseCommand(Of T)类中,我想使用以版本T返回结果的通用版本来重载它。不幸的是,VB.NET / C#编译器不允许您提供方法的重载,唯一的区别是返回类型。

由于这是一个受保护的函数,因此最终不会对整个API产生太大影响,但是仍然令我感到恼火的是,对于这种体系结构,我没有美观的解决方案。

暂时,我在BaseCommand(Of T)类中重写了非通用的InternalRunCommand,以便它调用一个新的受保护的抽象OnRunCommand函数,该函数采用相同的参数,但返回类型T的结果。InternalRunCommand也已声明不可覆盖。这也许是我能找到的最接近的-但想看看是否有更好的主意? :)

编辑:我已根据要求提供了代码的简化副本,以便您可以更好地可视化问题:

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

最佳答案

我想我找到了一个很好的模式,它允许您使用泛型函数覆盖此类函数的非泛型版本,并且没有像OP中那样的任何混乱的包装函数。

我已经用相同名称的受保护的ReadOnly属性替换了受保护的抽象InnerRunCommand函数。此属性的类型为Func(ICommand,Device,Object)。我修改了BaseCommand类的构造函数以接受这样的Func对象。

在Generic.BaseCommand(Of T)类中,我可以使用类似Func(Of ICommand,Device,T)类型的属性来遮盖InnerRunCommand。 Generic.BaseCommand(Of T)的构造函数类似地接受此类Func对象,并将该对象传递回非通用BaseCommand构造函数,而不会出现问题:)

我正在修改我的体系结构以支持这种新模式,并且如果我遇到任何问题都将通知大家。我欢迎对此方法提出任何批评,并欢迎其他回答:)

编辑:我在下面构造了一个简单的示例模式示例。我用C#而不是VB.NET编写。在C#中强制转换FUNC对象还涉及其他工作(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();
    }
}

关于c# - 泛型基类重写非泛型基类功能模式? (。净),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/52846094/

10-08 21:33