この API 関数があります:
public ResultEnum DoSomeAction(string a, string b, DateTime c, OtherEnum d,
string e, string f, out Guid code)
私はそれが好きではありません。パラメータの順序が不必要に重要になるからです。新しいフィールドを追加するのが難しくなります。何が渡されるのか確認するのが難しくなります。サブ関数ですべてのパラメータを渡すという別のオーバーヘッドが発生するため、メソッドをより小さな部分にリファクタリングするのが難しくなります。コードが読みにくくなります。
最も明白なアイデアを思いつきました。それは、各パラメータを 1 つずつ渡すのではなく、データをカプセル化するオブジェクトを用意してそれを渡すというものです。私が思いついたのは次のアイデアです。
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;
}
これにより、パラメータが多すぎるコンストラクタを回避し、不変性を実現できます。実際には、すべての問題 (パラメータの順序など) が修正されます。ただし、
- 誰もが(FXCopとJon Skeetを含む)同意するパブリックフィールドを公開するのは良くない。
- エリック・リッパートらは言う不変性のために読み取り専用フィールドに依存するのは嘘である。
そのとき私は混乱してしまい、次のような質問を書くことにしました。C# で可変性を導入せずに「パラメータが多すぎる」問題を回避する最も簡単な方法は何ですか? その目的で読み取り専用構造体を使用し、API 設計を悪くしないことは可能ですか?
説明:
- 単一責任の原則に違反していないと想定してください。私の元のケースでは、関数は指定されたパラメータを単一の DB レコードに書き込むだけです。
- 私は、与えられた関数に対する特定の解決策を求めているわけではありません。そのような問題に対する一般化されたアプローチを求めています。特に、可変性やひどい設計を導入することなく、「パラメータが多すぎる」問題を解決することに興味があります。
アップデート
ここで提供される回答には、それぞれ異なる利点/欠点があります。そのため、これをコミュニティ ウィキに変換したいと思います。コード サンプルと長所/短所を含む各回答は、将来同様の問題が発生した場合の優れたガイドになると思います。現在、その方法を見つけようとしています。
ベストアンサー1
ビルダーとドメイン固有言語スタイルの API (Fluent Interface) の組み合わせを使用します。API は少し冗長ですが、インテリセンスを使用すると入力が非常に速く、理解しやすいです。
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());