问题描述
我有这个 API 函数:
I have this API function:
public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d,
string e, string f, out Guid code)
我不喜欢.因为参数顺序变得不必要地重要.添加新字段变得更加困难.很难看到正在传递的内容.将方法重构为更小的部分更加困难,因为它会产生在子函数中传递所有参数的另一个开销.代码更难阅读.
I don't like it. Because parameter order becomes unnecessarily significant. It becomes harder to add new fields. It's harder to see what's being passed around. It's harder to refactor method into smaller parts because it creates another overhead of passing all the parameters in sub functions. Code is harder to read.
我想到了一个最明显的想法:让一个对象封装数据并传递它,而不是一个一个地传递每个参数.这是我想出的:
I came up with the most obvious idea: have an object encapsulating the data and pass it around instead of passing each parameter one by one. Here is what I came up with:
public class DoSomeActionParameters
{
public string A;
public string B;
public DateTime C;
public OtherEnum D;
public string E;
public string F;
}
这将我的 API 声明简化为:
That reduced my API declaration to:
public ResultEnum DoSomeAction(DoSomeActionParameters parameters, out Guid code)
不错.看起来很无辜,但实际上我们引入了一个巨大的变化:我们引入了可变性.因为我们之前所做的实际上是在堆栈上传递一个匿名的不可变对象:函数参数.现在我们创建了一个非常可变的新类.我们创造了操纵调用者状态的能力.那太糟糕了.现在我希望我的对象不可变,我该怎么办?
Nice. Looks very innocent but we actually introduced a huge change: we introduced mutability. Because what we previously had been doing was actually to pass an anonymous immutable object: function parameters on stack. Now we created a new class which is very mutable. We created the ability to manipulate the state of the caller. That sucks. Now I want my object immutable, what do I do?
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
}
}
如您所见,我实际上重新创建了我原来的问题:参数太多.很明显,这不是要走的路.我该怎么办?实现这种不变性的最后一个选择是使用只读"结构,如下所示:
As you can see I actually re-created my original problem: too many parameters. It's obvious that that's not the way to go. What am I going to do? The last option to achieve such immutability is to use a "readonly" struct like this:
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;
}
这使我们可以避免使用过多参数的构造函数并实现不变性.实际上它解决了所有问题(参数排序等).然而:
That allows us to avoid constructors with too many parameters and achieve immutability. Actually it fixes all the problems (parameter ordering etc). Yet:
- 所有人(包括 FXCop 和 Jon Skeet)都同意公开公共字段是不好的.
- Eric Lippert 等人说依靠只读字段来实现不变性是一个谎言.
那时我感到困惑并决定写这个问题:在 C# 中,在不引入可变性的情况下避免参数过多"问题的最直接方法是什么?是否可以为此目的使用只读结构并且没有糟糕的 API 设计?
That's when I got confused and decided to write this question: What's the most straightforward way in C# to avoid "too many parameters" problem without introducing mutability? Is it possible to use a readonly struct for that purpose and yet not have a bad API design?
澄清:
- 请假设没有违反单一职责原则.在我原来的情况下,该函数只是将给定的参数写入单个数据库记录.
- 我不是在寻求给定函数的特定解决方案.我正在寻求解决此类问题的通用方法.我对在不引入可变性或糟糕设计的情况下解决参数过多"问题特别感兴趣.
更新
此处提供的答案具有不同的优点/缺点.因此,我想将其转换为社区维基.我认为每个带有代码示例和优点/缺点的答案都会为将来的类似问题提供很好的指导.我现在正在研究如何去做.
The answers provided here have different advantages/disadvantages. Therefore I'd like to convert this to a community wiki. I think each answer with code sample and Pros/Cons would make a good guide for similar problems in the future. I'm now trying to find out how to do it.
推荐答案
框架中包含的一种风格通常是将相关参数分组到相关类中(但同样存在可变性问题):
One style embraced in the frameworks is usually like grouping related parameters into related classes (but yet again problematic with mutability):
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
这篇关于如何避免“参数过多"API设计中的问题?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!