LoginSignup
1
2

More than 3 years have passed since last update.

C#でBuilderパターン(effective java)

Last updated at Posted at 2020-02-19

コンストラクタの引数が多い

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パターン(Effective Java)

なんぞこれ。コンストラクタパラメータが異常に大杉る・・・。バカなの?死ぬの?そういう場合はBuilderパターンを検討してみよう。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2