小编典典

API设计中如何避免“参数过多”的问题?

all

我有这个 API 函数:

public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d, 
     string e, string f, out Guid code)

我不喜欢它。因为参数顺序变得不必要地重要。添加新字段变得更加困难。更难看到正在传递的内容。将方法重构为更小的部分更加困难,因为它会产生在子函数中传递所有参数的另一个开销。代码更难阅读。

我想出了一个最明显的想法:有一个对象来封装数据并传递它,而不是一个一个地传递每个参数。这是我想出的:

public class DoSomeActionParameters
{
    public string A;
    public string B;
    public DateTime C;
    public OtherEnum D;
    public string E;
    public string F;        
}

这将我的 API 声明减少到:

public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)

好的。看起来很天真,但我们实际上引入了一个巨大的变化:我们引入了可变性。因为我们之前所做的实际上是传递一个匿名的不可变对象:堆栈上的函数参数。现在我们创建了一个非常可变的新类。我们创造了操纵
调用者 状态的能力。太糟糕了。现在我希望我的对象不可变,我该怎么办?

public class DoSomeActionParameters
{
    public string A { get; private set; }
    public string B { get; private set; }
    public DateTime C { get; private set; }
    public OtherEnum D { get; private set; }
    public string E { get; private set; }
    public string F { get; private set; }

    public DoSomeActionParameters(string a, string b, DateTime c, OtherEnum d, 
     string e, string f)
    {
        this.A = a;
        this.B = b;
        // ... tears erased the text here
    }
}

正如你所看到的,我实际上重新创建了我原来的问题:参数太多。很明显,这不是要走的路。我要做什么?实现这种不变性的最后一个选择是使用这样的“只读”结构:

public struct DoSomeActionParameters
{
    public readonly string A;
    public readonly string B;
    public readonly DateTime C;
    public readonly OtherEnum D;
    public readonly string E;
    public readonly string F;        
}

这使我们可以避免使用过多参数的构造函数并实现不变性。实际上它解决了所有问题(参数排序等)。然而:

就在那时,我感到困惑并决定写下这个问题:在 C# 中避免“参数过多”问题而不引入可变性的最直接方法是什么?是否可以为此目的使用只读结构,但 API
设计不差?

澄清:

  • 请假设没有违反单一责任原则。在我原来的情况下,该函数只是将给定的参数写入单个数据库记录。
  • 我不是在寻找给定功能的具体解决方案。我正在寻求解决此类问题的通用方法。我特别有兴趣在不引入可变性或糟糕设计的情况下解决“参数过多”问题。

更新

这里提供的答案有不同的优点/缺点。因此,我想将其转换为社区
wiki。我认为每个带有代码示例和优点/缺点的答案都会为将来的类似问题提供很好的指导。我现在正试图找出如何做到这一点。


阅读 54

收藏
2022-08-27

共1个答案

小编典典

框架中包含的一种风格通常是将相关参数分组到相关类中(但又存在可变性问题):

var request = new HttpWebRequest(a, b);
var service = new RestService(request, c, d, e);
var client = new RestClient(service, f, g);
var resource = client.RequestRestResource(); // O params after 3 objects
2022-08-27