4

オプションを返す無効な引数を使用して、将来の実装が存在する可能性のあるすべてのインターフェイス(特性)メソッドを作成することをお勧めしますか?

例を挙げましょう。特性を持つ確率分布のライブラリを実装する場合

trait Similarity {
   def getDensity(): Double
}

ほとんどの分布は実空間全体で定義されていないため、ガウス分布の非正の分散など、常にいくつかの不正なパラメーターがあります。私がそれを正しく理解したならば、私はではOption[Double]なくを返し、Doubleを投げるべきIllegalArgumentExceptionです。

ほとんどの関数/計算についても同じことが言えると思います。この場合の「ベストプラクティス」とは何ですか?これにより、ライブラリが過度に不器用になるのではないかと心配しています。

ありがとう

4

4 に答える 4

6

問題となるのは引数ではなく、オブジェクトの状態であるため、IllegalArgumentExceptionをスローしません。例外である場合、IllegalStateExceptionが一致します。

ただし、実際の答えは、問題が発生した場合に発信者に何を期待するかによって異なります。

彼ら自身が例外をスローする場合、それはあなたがすべきことであり、彼らを煩わせることはありません。

答えが不可能であることに基づいて彼らが何か違うことをするなら、Option[Double]は良い指標です。

知っておく価値はあるが、役に立たない可能性はDouble.NaNであり、事実上NullオブジェクトですがDoubles用です。

于 2012-05-30T12:11:49.937 に答える
3

答えは主にスタイルと意図の問題です。

エラーが実際に例外条件である場合、例外をスローしても実際には何も問題はありません。ArithmeticException例外をスローすることを決定した場合は、問題が何であるかをよりよく示すため、、または作成したそのサブクラスをスローすることをお勧めします。

これは頻繁に発生する可能性のある種類のエラーであり、呼び出し元が最も適切に処理しますか?それとも、これは上位層で処理するのが最適な、よりまれな状況ですか?あるいは、データやコーディングの問題など、より根本的なエラーの指標でさえありますか?

比較のために、整数を0で除算することは無効ですが、除算するたびにすべての人にそれに対処するように強制すると、すぐに古くなります。これは、提供しているデータで例外がスローされないことが確実にわかっている場合に特に当てはまります。書くことを想像してみてくださいx / 2 + 5

// normally
x / 2 + 5

// divide returns Option[Int]
(x / 2).map(_ + 5).get

// divide returns Either[ArithmeticException, Int]
(x / 2).right.map(_ + 5).right.get

このエラーが発信者が処理できるものであり、処理する必要があるものである場合は、Option[Double]またはそれEither[someErrorClass, Double]が適切です。

Optionなぜ失敗したのか、無効なのかを気にしないのであれば、それだけでいいのです。発信者が対処するのもかなり簡単です。

Either失敗する理由が複数ある場合に適しています。発信者がその理由を知ることが重要です。ただし、これは、よりも処理が少し難しい場合Optionがあります。

于 2012-05-30T14:48:58.933 に答える
2

すでに与えられた答えに加えて:

Optionタイプが常に唯一のオプションであるとは限りません。そして、ほとんどのインターフェースがを返すようにすることOptionは確かに望ましくありません。通常、呼び出し元がメソッドが「何も」を返さないことを期待できる場合にのみ望ましいです。

優れたAPIを設計するには、さらに検討が必要です。あなたの特徴とクラスを見てください。これまたはそのプロパティを提供できない場合、それらは完全ですか?-特定のプロパティがないと完全ではない場合、プロパティはOption値であってはなりません。むしろ、次のように言うかもしれません。プロパティを提供できない場合、オブジェクトは別のタイプになります。

例として、ボードゲームのマップを表すグリッドについて考えてみます。Cellすべてのフィールドはデータ型で表されます。一部のセルには色が付いている場合があります。

APIの最初のバージョンは次のようになります。

trait Cell {
  def color:Color
}

さて、ある時点で、いくつかのセルに色がないことに気付くでしょう。たとえば、空のセル。または、デフォルトのGUIカラーでレンダリングされることになっているテキストのみを含むセル。

したがって、このバージョンを検討することになります。

trait Cell {
  def color:Option[Color] = None
}

現在、必要に応じて、すべての実装でプロパティCellを自由にオーバーライドできます。colorしかし、それが唯一の可能な解決策ではありません。

この代替案を考えてください:

trait Cell { }
trait ColoredCell extends Cell {
  def color:Color
}

これで、セルのタイプによって色があるかどうかが決まり、色付きのセルには色が強制されます。

UIレイヤーの一部のコードには、次のようなフラグメントが含まれている可能性があります。

...
val cell:Cell = grid cellAt coordinates
val uiComponent:JComponent = ...
cell match {
  case coloredCell:ColoredCell => uiComponent setColor coloredCell.color
  case _ => // No color assigned
}

このアプローチは、不変オブジェクトで最適に機能します。それらは変更されることはなく、変更されたバージョンを表す新しいインスタンスを返します。

たとえば、すべてのセルに色を設定するメソッドがある場合があります。

trait Cell {
  def withColor(color:Color):ColoredCell
}

このセルのコピーを表す新しいセルを別の色で返します。もちろん、これがのインスタンスになることはすでにわかっているColoredCellので、メソッドコントラクトに入れます。

このアプローチがうまく機能する場合もありますが、モデルにどの程度適合するかを事前に注意深く確認する必要があります。

于 2012-05-30T14:09:14.513 に答える
2

一般に、APIの場合、常に定義されている場合はDoubleを返し、有効な入力値に対して定義されていない場合はOption[Double]を返します。無効な入力に対して未定義の場合は、Either [someError、Double]を使用します。ここで、無効な入力に対して左が返されます。(またはいずれかに類似しているscalazからの検証)。

私がやらないと思うのは、nullを返すか、例外をスローすることです。Leftがエラーを示しているEitherを返す場合、左側のエラーはThrowable(IllegalArgumentExceptionやIllegalStateExceptionなど)である可能性がありますが、スローすることは避けます。

于 2012-05-30T12:31:10.950 に答える