c# - API设计中如何避免 "too many parameters"问题?

标签 c# data-structures immutability

我有这个 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)

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

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。我认为每个带有代码示例和优点/缺点的答案都将为将来类似问题提供很好的指导。我现在正试图找出如何去做。

最佳答案

结合使用构建器和特定领域语言风格的 API——Fluent Interface。 API 有点冗长,但使用 intellisense 可以非常快速地输入并且易于理解。

public class Param
{
        public string A { get; private set; }
        public string B { get; private set; }
        public string C { get; private set; }


  public class Builder
  {
        private string a;
        private string b;
        private string c;

        public Builder WithA(string value)
        {
              a = value;
              return this;
        }

        public Builder WithB(string value)
        {
              b = value;
              return this;
        }

        public Builder WithC(string value)
        {
              c = value;
              return this;
        }

        public Param Build()
        {
              return new Param { A = a, B = b, C = c };
        }
  }


  DoSomeAction(new Param.Builder()
        .WithA("a")
        .WithB("b")
        .WithC("c")
        .Build());

关于c# - API设计中如何避免 "too many parameters"问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6239373/

相关文章:

c# - datagridview 行点击

data-structures - 保持多维整数框的质心并删除一些 "orthants"

java - 这个类是完全不可变的吗?

c# - 将纬度和经度发送到表单(提交到数据库)

c# - 如何在 Visual Studio 2017 项目(新的 .csproj 文件格式)中设置 `OutputPath` 而目标框架不会混淆已解析的路径?

c# - 为什么单独的方法调用被认为是同一个任务?

c - 哪种使用 C 数组的数据组织可以生成最快的代码,为什么?

algorithm - B-Tree - 为什么不能有一个节点的键数是偶数?

python - 为什么 Python 中的元组使用 reversed 但没有 __reversed__?

c# - C#/C++ 中缓存或预计算的不可变函数