简体   繁体   English

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

[英]Interface based design and optional method parameters

I am creating a C# wrapper library for a new Web API. 我正在为新的Web API创建一个C#包装器库。

The service provides a set of a few APIs, where each method receives several mandatory parameters, and a few optional ones (each method may receive different mandatory/optional parameters). 该服务提供了一组API,其中每个方法接收几个必需参数,以及一些可选参数(每个方法可以接收不同的强制/可选参数)。

The parameters are sent in a POST method, as a long string of param=value&param2=value2&.... 参数在POST方法中发送,作为param = value&param2 = value2&....的长字符串。

Having gotten used to interface based design -- is it a good fit in this case? 习惯了基于接口的设计 - 在这种情况下它是否适合?

I fail to find a good solution for mapping out ALL of the APIs methods and parameters into a single interface, without creating a of method overloads, creating a big mess, which would make it harder for the user to use. 我没有找到一个很好的解决方案,可以将所有API方法和参数映射到单个接口中,而不会产生方法重载,从而造成很大的混乱,这会使用户更难以使用。

Example of what it may look like: 它的外观示例:

public interface IServiceAPI
{
    void MethodA(string mandatory, string mandatory2);
    void MethodA(string mandatory, string mandatory2, int optional);
    void MethodB(string mandatory);

    ... etc
}
  • I am aware of optional parameters that were introduced in .NET 4. This is not a good solution as this library is targeted for lower versions of .NET, and also because "optional parameters" are really just a way to set default values, and not for NOT SENDING any value for the parameter. 我知道.NET 4中引入的可选参数。这不是一个好的解决方案,因为这个库是针对较低版本的.NET,也是因为“可选参数”实际上只是一种设置默认值的方法,并且不是为了发送参数的任何值。

Is Interface based design may be a good fit here? 基于接口的设计是否适合这里? or to put it differently - where does interface based design best fits? 或换句话说 - 基于界面的设计最适合哪里?

First, I don't think designing an interface with a lot of overloads is a good idea. 首先,我不认为设计具有大量重载的接口是个好主意。 Unless the functions do something different you should just leave it up to whoever uses the interface if they want to put in some default values. 除非函数执行不同的操作,否则只要想要输入一些默认值,就应该将它留给使用界面的人。

That said, secondly the entity that you're writing seems like it would serve you better as a base class. 那就是说,其次你正在写的实体似乎会更好地为你提供基类。 The name ServiceAPI lends itself to imply at least some amount of standard functionality. ServiceAPI这个名称有助于暗示至少一些标准功能。 And this way you could have the multiple overloads and just let any child classes override the primary method. 通过这种方式,您可以拥有多个重载,并让任何子类覆盖主方法。

Interfaces are the right way to go but I think you're using the wrong one. 接口是正确的方法,但我认为你使用的是错误的接口。 I would create an IOptionalParameter interface as follows: 我将创建一个IOptionalParameter接口,如下所示:

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

Then you can have just one method exposed in your API with each argument being of type IOptionalParameter. 然后,您可以在API中公开一个方法,每个参数的类型为IOptionalParameter。

This will also make the code you use for constructing the url request string neater. 这也将使您用于构建url请求字符串的代码更整洁。 If it makes sense, you can add a Name property to the interface too thus simplifying it even further. 如果它有意义,您可以向界面添加Name属性,从而进一步简化它。

Update 更新

To summarise the three different approaches and the trade-off between them: 总结三种不同的方法以及它们之间的权衡:

  1. Overloads - it is clearer that parameters are optional but leads to confusion as to potential differences in implementation and makes the implementation a bit messier 过载 - 参数是可选的更清楚,但会导致实现中的潜在差异混乱,并使实现有点混乱
  2. Nullable types - not very clear that the parameters are optional but cleaner on the implementation side 可空类型 - 不太清楚参数是可选的但在实现方面更干净
  3. IOptionalParameter - explicitly states that parameters are optional or not and implementation is clean but horrible to call from the client side IOptionalParameter - 明确指出参数是否可选,实现是干净的,但从客户端调用可怕

It seems that your methods tend to have multiple parameters. 看来您的方法往往有多个参数。 To my experience, 3 arguments should be maximum. 根据我的经验,3个参数应该是最大的。 To solve this issue (and as a side-effect find a solution for optional/required parameters) I'd suggest packing parameters into a class: 要解决这个问题(并且作为副作用找到可选/必需参数的解决方案),我建议将参数打包到类中:

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;
    }
}

As you can see, such a design enforces passing required parameters (you cannot create an argument instance without specifying them) and allows adding optional parameters. 如您所见,这样的设计强制传递所需的参数(您无法在不指定参数的情况下创建参数实例)并允许添加可选参数。 Of course if some methods share the same subset of arguments then you ought to inherit parameter classes. 当然,如果某些方法共享相同的参数子集,那么您应该继承参数类。

If null values are also relevant, then previously mentioned IOptionalParameter seems to be necessary (combined with this parameter class approach). 如果空值也是相关的,则前面提到的IOptionalParameter似乎是必要的(与此参数类方法结合使用)。

Please bear in mind that your code should be SOLID . 请记住,您的代码应该是SOLID When I look at the examples you provided I'm concerned about Single Responsibility Principle. 当我看到您提供的示例时,我会关注单一责任原则。 Of course without knowing the actual code I'm only guessing, so treat it as a piece of advice. 当然,如果不知道我只猜测的实际代码,那么请将其视为一条建议。

Nullable data types? 可以为空的数据类型?

For example instead of 例如,而不是

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

You could simplify this to 你可以简化这个

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

However, if the optional parameter was a reference type then it may not be as obvious to the caller that they could just pass null to it. 但是,如果可选参数是引用类型,则调用者可能不会明显地将null传递给它。

If you have a lot of optional parameters, like v oid MethodC(string mandatory, string mandatory2, string optional1, int? optional1, string optional2); 如果你有很多可选参数,比如v oid MethodC(string mandatory, string mandatory2, string optional1, int? optional1, string optional2); you don't want to provide signatures for all the possible combinations, you could provide simply: 您不想为所有可能的组合提供签名,您可以简单地提供:

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.

I'm not sure why you want the as an interface as opposed to a regular class. 我不确定为什么你想要一个接口而不是常规类。 Will other classes implement this interface, or are you just looking for a standard way to access the API? 其他类是否会实现此接口,或者您只是在寻找访问API的标准方法?

If you're just looking for a standard way to access the API, I'd suggest a variation on the builder pattern. 如果您只是在寻找访问API的标准方法,我建议使用构建器模式的变体。 The builder pattern is normally used for classes, but I don't see why it can't be used for methods as well. 构建器模式通常用于类,但我不明白为什么它也不能用于方法。 See http://cdmckay.org/blog/2009/07/03/joshua-blochs-builder-pattern-in-csharp/ for a couple class-based examples. 有关基于类的示例,请参见http://cdmckay.org/blog/2009/07/03/joshua-blochs-builder-pattern-in-csharp/

Here's my attempt given what you supplied. 这是我对你提供的东西的尝试。 I apologize if there are syntax errors, my editor at home is somewhat lacking... 如果有语法错误,我很抱歉,我家里的编辑有点缺乏......

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);
        }
    }
}

The client would then call the method like this: 然后客户端会调用这样的方法:

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

If they don't want to set a value for an optional parameter, they can just skip it. 如果他们不想为可选参数设置值,他们可以跳过它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM