- ベストアンサー
オブジェクト生成時のセオリーについて
- オブジェクト指向言語において、コンストラクタの返り値はほとんど存在しません。また、コンストラクタで例外を投げることは推奨されません。
- しかし、コンストラクタに引数を渡してインスタンスを生成するクラスの場合、不十分な引数を渡すと使用できないインスタンスが生成されることがあります。
- このような状況において、適切な引数が渡されることを確認するのは生成されるクラス自身か、外部の処理(ファクトリー的なクラス・メソッド)かは一般的なセオリーではありません。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
まずはじめに、コンストラクタで例外を投げることが推奨されていない言語は、C++です。 これは、コンストラクタ呼び出しが終了したあとにそのインスタンスを変数に代入する、という仕様のためで、コンストラクタの実行に失敗した時(例外が投げられた時等)にはそのインスタンスがどこからも参照されない状態が生まれてしまい、健全なGCを持たないC++においてメモリリークが発生してしまうためです。 また、バッチ処理向けプログラム等の短時間実行させるプログラムでは、メモリリークは大した問題にはならないことが多いです。当てにできるわけではありませんが、実行終了時にOSが回収してくれるので。 ですので、C++を使うのであれば、私なら出来る限り C* a = new C(x,y,z); if (a->invalid()) {cout << "construction failed." << endl; delete a; return;} というような処理にし、そうでなければ素直に例外を投げます。例外機構は、トートロジーですが、例外的なことに対処するための機構ですので。 > ただ、最近知り合いから「クラスはできるだけ不変な状態を保つよう設計した方がいい。そのほうが使いやすく、再利用時のバグが減る」という話を聞いて状態を持たないクラスの設計というのを考えこんでしまい 「状態を持たない」というのと、「状態が不変に保たれている」というのは大分違います。状態を持たなくていいならクラスではなく関数として実装すべきです。後者は、ある初期状態をただ一度だけ取得し、以降その状態を保持したまま計算を続ける、という意味でしょう。 例えば、整数を保持するクラスNumberがあるとして、そのメソッドにsetterやoperator+=等の破壊的作用を持つメソッドがなければ、再利用時のバグが激減することでしょう。代入を始めとする破壊的作用は、全て効率化のためのテクニックであり、実行時間や実行時使用メモリ空間を気にしなければ、初期化時以外の代入を殆ど使わずにプログラミングする事が出来るはずです。 ただし、自分自身ともどの引数とも異なる結果をそのメソッドから得たい場合には、メソッド内部で新しくインスタンスを生成する必要が出ます。
その他の回答 (1)
- MARU4812
- ベストアンサー率43% (196/452)
VB(VB.NET)メインでやってきたプログラマの意見です。 VB6時代からやってますが、以下の内容はVB.NETを基準にしてます。 本来のコンストラクタで設定されるのは、画面上のコントロールの 初期値設定くらいで、デザイン画面で設定したものがインスタンスを 生成する上で致命的なエラーを起こす事は少ないと思います。 本来のコンストラクタを呼び出して、追加で何か初期表示などを変更 する場合、インスタンスの生成は本来のコンストラクタで成功し、 その後の初期設定の関数実行でエラーが出たと見なすことができると 思います。 あくまで、プログラマが追加したプログラムの不具合ですので、 それでインスタンスの生成が失敗するのはバグという認識です。 インスタンスの生成自体は(どうしても必要ならデフォルト値を 用意して)つねに成功させるべきではないでしょうか? 実装としては、 1)Shared な関数でインスタンスの生成及びエラー判定までして 合否を戻り値として返す。(成功時はインスタンスの参照アドレス 失敗時は Nothing など) 2)インスタンスの生成は、エラー時はデフォルト値などで置き換えて 生成し、合否を返す関数を別途用意する。 実行時は、インスタンスの生成と合否判定関数の2つをセットで 実行する仕様とする。 (※コーディング時に合否判定関数を抜かす人は必ず出てくるけど、 デフォルト値でとりあえずは問題なく動くようにしておく) 3)インスタンスの生成はエラー時はデフォルト値などで置き換えて 生成し、失敗時はインスタンス使用時(関数実行時)に失敗した旨を メッセージ表示し、その後の関数の処理は実行しないようにする。 (※簡単に実現しようとすると全ての関数の先頭で合否関数を呼ぶので ちょっと面倒) 4)失敗した時はインスタンスを生成せず、そのままではエラー内容の 詳細が不明なので(呼び出し側でインスタンス未生成エラーになるので) システム全体で共通のエラーを格納する場所へ格納する。 (※一昔前の API の GetLastError 関数みたいな仕様。2と同じく、 インスタンス生成とエラー判定の2個で1セットだけど、エラー判定 を抜かしたら、呼び出し側のインスタンス未生成エラーになるので そこで未生成エラーに対するエラー処理を書いておく) 私の意見としては番号が若い方がお勧め。
お礼
回答ありがとうございます。 インスタンス生成時の引数の健全性の評価は、生成されるインスタンスの元となるクラスが保証するべきだ、という考え方でよろしいでしょうか。 C++以外のオブジェクト指向言語ならば例外を投げて対処、C++ならばIsValidのような健全性確認用の関数を設けてチェックということですね。 不変オブジェクトについては、ちょっと勉強不足でした。関数型言語が持つようなパラダイムを一概にオブジェクト指向に取り入れるのは難しいんですね。