主題の件ですが、関数の返り値として、「結果」ともし失敗した場合は「理由」も返すというような場合に、どのように実装すればよいのか悩んでいます。
説明が難しいので具体例を挙げて質問させていただきます。
具体例
- 社員データベースのようなものがあって、
名前
と生年月日
を引数で与えると、その人の社員番号
を返す関数があったとします - その関数は、データベースに登録されている
名前
と生年月日
があれば、正常に社員番号
を返しますが、データベースに登録されていない場合、失敗を返します。 - その失敗の理由として、例えば「該当の名前なし」とか「該当の生年月日なし」とかが考えられます。
- 上記の理由で関数が失敗した場合は、呼び出し元にその失敗理由も返します。
(そもそもそんな関数作らないという話もあるでしょうが、あくまで例ですのでご容赦ください。)
質問
このような条件の場合、例えば C# であれば、どのような実装がスマートなのでしょうか。
今まで業務では基本的にC言語ばかりを扱ってきたので、モダンなやり方がわからなくて困っています。
C言語的にやるならば、「成功可否のBOOL値を関数の返り値として、失敗理由をポインタ渡しして・・・」とかがありそうですが。
自分で考えてみた選択肢
① out
修飾子を使う
例えば以下のような感じです。なんか手続き型チックで違和感がありますが、一番直?な気もします。
cs
1class EmployeeNumber 2{ 3 int Value {get;} 4 5 public EmployeeNumber(int value) 6 { 7 Value = value; 8 } 9} 10 11// 成功した場合は返り値として社員番号を返す。失敗した場合はnullを返して、FailedReasonに失敗理由を設定して返す。 12EmployeeNumber GetEmployeeNumberBy(string name, DateTime birthDay, out FailedReason failedReason); 13// 本当にやるなら EmployeeNumber をヌルオブジェクトパターン適用すべきですが省略
② タプルで返す
例えば以下のような感じです。これも手続き型脳的には素直な気がします。
cs
1class EmployeeNumber 2{ 3 int Value {get;} 4 5 public EmployeeNumber(int value) 6 { 7 Value = value; 8 } 9} 10 11// 成功した場合は返り値として (社員番号, null) を返す。失敗した場合は (null, 失敗理由) を返す。 12(EmployeeNumber employeeNumber, FailedReason? failedReason) GetEmployeeNumberBy(string name, DateTime birthDay); 13// 本当にやるなら EmployeeNumber をヌルオブジェクトパターン適用すべきですが省略
③ 社員番号クラスの中に失敗理由を埋め込む
例えば以下のような感じです。
cs
1class EmployeeNumber 2{ 3 int Value {get;} 4 FailedReason? FailedReason {get;} 5 6 public EmployeeNumber(int value, FailedReason? failedReason=null) 7 { 8 Value = value; 9 FailedReason = failedReason; 10 } 11} 12 13EmployeeNumber GetEmployeeNumberBy(string name, DateTime birthDay);
④ ダブルディスパッチパターンを使用する
個人的にはこの方法が一番マシなのかなぁと思っています。
cs
1class EmployeeNumber 2{ 3 int Value {get;} 4 5 public EmployeeNumber(int value) 6 { 7 Value = value; 8 } 9} 10 11interface IGetEmployeeNumberFailed 12{ 13 void OnFailedBecauseNotMatchedName(); 14 void OnFailedBecauseNotMatchedBirthDay(); 15} 16 17// 成功した場合は返り値として社員番号を返す。失敗した場合は null を返して失敗理由を与えられたインターフェイスを使用してコールバックする。 18EmployeeNumber GetEmployeeNumberBy(string name, DateTime birthDay, IGetEmployeeNumberFailed onFailed);
⑤ ドメインイベントを発行する
DDD でやるならこれなんでしょうが、個人的にはメリットがあまりピンとこないです。
cs
1class EmployeeNumber 2{ 3 int Value {get;} 4 5 public EmployeeNumber(int value) 6 { 7 Value = value; 8 } 9} 10 11// 成功した場合は返り値として社員番号を返す。失敗した場合はドメインイベントを発行する。 12EmployeeNumber GetEmployeeNumberBy(string name, DateTime birthDay) 13{ 14 // ~~ 省略 ~~ 15 16 // 詳細には書ききれないので、かなり省略します 17 if(名前が見つからなかった) 18 domainEvent.Publisher(new DomainEvent<FailedReason>(FailedReason.NotMatchedName)); 19 if(誕生日が見つからなかった) 20 domainEvent.Publisher(new DomainEvent<FailedReason>(FailedReason.NotMatchedBirthDay)); 21 22 return 社員番号 23}
長くなりましたが
長文となり申し訳ありませんが、「①~⑤のうち●番が良い」とか、「こういう方法が良い」等、ご教授のほど、宜しくお願い致します。
また、これが良いとは言わないまでも、普段こうしているというのがあれば教えていただけると幸いです。
回答9件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/07/26 13:34
2018/07/26 13:35
2018/07/27 03:07
2018/07/27 03:40
2018/07/27 08:58
2018/07/27 09:20
2018/07/27 09:26 編集
2018/07/27 12:42
2018/07/27 12:46
2018/07/27 12:57
2018/07/27 13:12
2018/07/28 03:57
2018/07/28 04:16
2018/07/28 06:48
2018/07/28 08:01
2018/07/28 13:37 編集
2018/07/28 21:52 編集
2018/07/29 00:26
2018/07/29 02:27