基于接口的设计和可选方法参数

本文关键字:参数 方法 于接口 接口 | 更新日期: 2023-09-27 18:33:43

我正在为新的Web API创建一个C#包装库。

该服务提供了一组几个 API,其中每个方法接收多个必需参数和一些可选参数(每个方法可能接收不同的强制/可选参数(。

参数在 POST 方法中发送,作为一长串 param=value&param2=value2&...。

已经习惯了基于界面的设计 - 在这种情况下它是否合适?

我未能找到一个好的解决方案将所有 API 方法和参数映射到单个接口中,而不会创建方法重载,造成大混乱,这将使用户更难使用。

它可能看起来像什么样子的示例:

public interface IServiceAPI
{
    void MethodA(string mandatory, string mandatory2);
    void MethodA(string mandatory, string mandatory2, int optional);
    void MethodB(string mandatory);
    ... etc
}
  • 我知道 .NET 4 中引入了可选参数。这不是一个好的解决方案,因为此库面向较低版本的 .NET,还因为"可选参数"实际上只是设置默认值的一种方式,而不是不发送参数的任何值。

基于界面的设计可能很适合这里吗?或者换句话说 - 基于界面的设计最适合在哪里?

基于接口的设计和可选方法参数

首先,我不认为设计一个有很多重载的界面是一个好主意。除非函数执行不同操作,否则如果使用接口的人想要输入一些默认值,则应将其留给使用接口的人。

也就是说,其次,您正在编写的实体似乎可以更好地作为基类为您服务。ServiceAPI 这个名称本身就意味着至少一定数量的标准功能。这样,您可以拥有多个重载,并让任何子类覆盖主方法。

似乎您的方法倾向于具有多个参数。根据我的经验,最多应该有 3 个参数。为了解决这个问题(并作为副作用找到可选/必需参数的解决方案(,我建议将参数打包到一个类中:

class MethodAParameters
{
    public string Required1 {get;set;} //add validation in setter (nulls are not allowed)
    public string Required2 {get;set;} //add validation in setter (nulls are not allowed)
    public int? Optional1 {get;set;} //nulls allowed
    public MethodAParameters(string required1, string required2)
    {
        Required1 = required1;
        Required2 = required2;
    }
}

如您所见,这样的设计强制传递必需的参数(如果不指定参数,则无法创建参数实例(并允许添加可选参数。当然,如果某些方法共享相同的参数子集,那么您应该继承参数类。

如果 null 值也是相关的,那么前面提到的 IOptionalParameter 似乎是必要的(结合此参数类方法(。

请记住,您的代码应该是可靠的。当我查看您提供的示例时,我担心的是单一责任原则。当然,在不知道实际代码的情况下,我只是猜测,所以把它当作一条建议。

接口是正确的方法,但我认为你用错了。 我将创建一个IOptionalParameter接口,如下所示:

interface IOptionalParameter<T>
{
    public bool IsSet {get;}
    public T Value {get;}
}

然后,您可以在 API 中只公开一个方法,每个参数的类型为 IOptionalParameter。

这也将使您用于构造 url 请求字符串的代码更整洁。 如果有意义,您也可以向接口添加 Name 属性,从而进一步简化它。

更新

总结三种不同的方法以及它们之间的权衡:

  1. 重载 - 更清楚的是,参数是可选的,但会导致对实现中潜在差异的混淆,并使实现更加混乱
  2. 可为空的类型 - 不太清楚参数是可选的,但在实现端更干净
  3. IOptionalParameter - 明确声明参数是否可选,实现干净,但从客户端调用很糟糕

可为空的数据类型?

例如,代替

void MethodA(string mandatory, string mandatory2);
void MethodA(string mandatory, string mandatory2, int optional);

您可以将其简化为

void MethodA(string mandatory, string mandatory2, int? optional);

但是,如果可选参数是引用类型,则调用方可能不太明显,他们可以只向其传递 null。

如果你有很多可选参数,比如 v oid MethodC(string mandatory, string mandatory2, string optional1, int? optional1, string optional2);你不想为所有可能的组合提供签名,你可以简单地提供:

MethodC(string mandatory, string mandatory2) // for people that just want the basic functionality
MethodC(string mandatory, string mandatory2, string optional1, int? optional1, string optional2); // for people that want to specify extra, and they can pass null for some of the optional ones if they like.

我不确定为什么你想要作为一个接口而不是一个常规类。其他类会实现此接口,还是您只是在寻找访问 API 的标准方法?

如果您只是在寻找一种访问 API 的标准方法,我建议您对构建器模式进行变体。构建器模式通常用于类,但我不明白为什么它也不能用于方法。有关几个基于类的示例,请参阅 http://cdmckay.org/blog/2009/07/03/joshua-blochs-builder-pattern-in-csharp/。

鉴于您提供的内容,这是我的尝试。如果有语法错误,我深表歉意,我家里的编辑器有些欠缺......

public class AccessServiceAPI
{
    private void MethodA(string mandatory, string mandatory2, string optional)
    {
        // do stuff
    }
    public class MethodABuilder
    {
        private string Mandatory { get; set; }
        private string Mandatory2 { get; set; }
        private string Optional { get; set; }
        public MethodABuilder( string mandatory, string mandatory2)
        {
            Mandatory = mandatory;
            Mandatory2 = mandatory;
            Optional = "default value";
        }
        public MethodABuilder Optional( string optional )
        {
            Optional = optional;
            return this;
        }
        public void Build()
        {
            MethodA(mandatory, mandatory2, optional);
        }
    }
}

然后,客户端将像这样调用该方法:

MethodABuilder.Builder(mandatory, mandatory2).Optional(optional).Build();

如果他们不想为可选参数设置值,则可以跳过它。