めあとるーむ記録帳

なんか書く

コンストラクタの引数がいいのか、setterを使った方がいいのか

クラス変数やメンバ変数を置くとき、当然その変数に何らかの値を入れるものである。

で、その変数になにか値を入れるタイミングはコンストラクタで入れるべきなのか、それとも呼び出しもとでsetterを呼び出して入れるべきなのか、どちらがいいのかわからなくなった。わからないなりになにか文章に直そうと思ってこの記事を記す。

わからないので、もしこの記事で「バカジャネーノ」とか思った方がいれば教えてほしい。教えて。

コンストラクタで渡す場合

要は

Object obj = new Object(hoge);

みたいなケースである。

これが強制されるケースはJavaであればfinal修飾子を、C#であればreadonly修飾子を付けた変数はコンストラクタで値を決めるか、変数宣言時に値を決定する必要がある。ちなみにC#のconstは宣言時に値を決定しなければならない。

逆に言えばコンストラクタで値を決定できるともいえる。

メリット

  • setterを作らないことでクラス変数やメンバ変数のアクセス制御ができる
  • 呼び出し側に記述を作らない(ケース次第)

setterで渡す場合

コードに直すと

Object obj = new Object();
obj.setHoge(hoge);

というケース。

これコンストラクタが強制されるケース以外特に制限はない。

メリット

  • setterで制限や値の調整ができる
  • 呼び出し側に記述を作る(ケース次第)
  • 外部からの値の操作ができる(あんまりよくない)

比較

各々のメリットの欄に書いたが、呼び出し側に記述を作ることが場合によっては良かったり悪かったりする。

例えばゲームプログラミングで敵キャラを生成するときにHPなどの各ステータスをいちいちsetterで渡すのは正しいとは言えない(もっともこの辺は敵の種類を渡したら一発で処理できるようにすべきだし、そうであってもコンストラクタに渡せばいけるはずである)

Enemy enemy = new Enemy(enemyType);
// もっと正しくするにはcreateEnemy(enemyType)とかFactoryパターンやBuilderパターンを使うべきだと思う

こういったケースならコンストラクタに渡すべきだろう。

また、逆に言えば呼び出し側が呼び出すべき内容であればsetterでおくべきだということである。

外部からの操作に関していえば、setterであっても制限もできる。

void setHoge(Object hoge){
    if(hoge!=null){
        this.hoge = hoge;
    }
}

とすれば、nullでない場合だけ入るという形にできる。要は値が設定されていない場合だけ入るという形で、あとから外部で値を変更するというのは通じないことになる(どこかでnullになるような処理でない限り)。

総括

もっと言えば、極論はコードの書き方次第だしある程度の思想が統一された状態で書けばどちらでもいいのだと思う。

思うのだが、いかんせんなんかいい感じの資料とかもないのでいまいちどうやればいいのかわからないという感じはする。