コンストラクタの引数が多い
Studentクラスは名前と誕生日を必須の情報として持っていて、オプションでニックネームがついている。
こんなクラスを使いたいと思いました。
var taro = new Student("太郎", new DateTime(1990, 1, 1));
var sachiko = new Student("幸子", new DateTime(1990, 1, 1), "さっちゃん");
必須2個と任意1個の引数だからまだいいけど、増えてくるととても大変になります。
そして任意のパラメータ分のオーバーロードを書きたくない。
メソッドチェーンで書きたい
コンストラクタを呼ぶときにパラメータを順次設定するメソッドを呼び出したいですね。
var taro = Student.Builder.Instance
.SetName("Taro")
.SetBirth(new DateTime(1995, 4, 18))
.SetNickname("タロ")
.Build();
var sachiko = Student.Builder.Instance
.SetName("幸子")
.Build(); // 誕生日をセットしていないのでコンパイルエラーにしたい
SetNameとSetBirthは必須だがSetNicknameは呼ばなくてもbuildしたい。
これは以下のコードで実現できます。
public class Student
{
public string Name { get; }
public DateTime Birth { get; }
public string Nickname { get; }
private Student(Builder builder)
{
Name = builder.Name;
Birth = builder.Birth;
Nickname = builder.Nickname;
}
public interface IHasName
{
IHasBirth SetName(string name);
}
public interface IHasBirth
{
Builder SetBirth(DateTime birth);
}
public sealed class Builder : IHasName, IHasBirth
{
internal string Name { get; private set; }
internal DateTime Birth { get; private set; }
internal string Nickname { get; private set; } = "";
public static IHasName Instance
{
get { return new Builder(); }
}
private Builder() { }
public IHasBirth SetName(string name)
{
Name = name;
return this;
}
public Builder SetBirth(DateTime birth)
{
Birth = birth;
return this;
}
public Builder SetNickname(string nickname)
{
Nickname = nickname;
return this;
}
public Student Build()
{
return new Student(this);
}
}
}
StudentとBuilderクラスのコンストラクタはprivateにしてあるので、Instanceを介さないとインスタンス化できません。
InstanceからはIHasNameが返るのでSetName以外は呼べません。
そしてSetNameからはSetBirthしか呼べません。
SetBirthからはSetNicknameもできるし設定せずにインスタンス化もできます。
そしてStudentインスタンスはreadonlyなフィールドだけを持つので、どこかで変更されることもありません。必要に応じてメソッドを追加していきましょう。
参考
なんぞこれ。コンストラクタパラメータが異常に大杉る・・・。バカなの?死ぬの?そういう場合はBuilderパターンを検討してみよう。