IllegalStateExceptionまたはサイレントメソッド実行の方が良いですか?[閉まっている]


16

play()メソッドとstop()メソッドを持つMediaPlayerクラスがあるとします。playメソッドが以前に呼び出されていない場合にstopメソッドを実装するときに使用する最適な戦略は何ですか。2つのオプションがあります。プレーヤーが適切な状態にないために例外をスローするか、stopメソッドの呼び出しを静かに無視します。

ある状況でメソッドが呼び出されることは想定されていないが、その実行がプログラム全般に害を及ぼさない場合の一般的なルールは何でしょうか?


21
例外を使用して、例外的でない動作をモデル化しないでください。
アレクサンダー-モニカーを

1
おい!重複しているとは思わない。私の質問はIllegalStateExceptionに関するものであり、上記の質問は一般的な例外の使用に関するものです。
x2bool

1
静かに失敗するだけでなく、異常を警告としてログに記録してみませんか?使用しているJavaは、さまざまなログレベル(ERROR、WARN、INFO、DEBUG)をサポートしています。
エリックイーストランド

3
アイテムが既にセットにある場合、Set.addは例外をスローしないことに注意してください。その目的は、「要素をセットに追加する」のではなく、「要素がセットにあることを確認する」ことであるため、要素がすでにセットにある場合は何もしないことで目的を達成します。
user253751

2
今日の単語:冪等
ジュール・

回答:


37

ルールはありません。APIを「感じさせる」方法次第です。

個人的には、音楽プレーヤーで、私は状態からだと思うStoppedStopped方法によるがStop()完全に有効な状態遷移です。あまり意味はありませんが、有効です。これを念頭に置いて、例外をスローすることは、つまらなく不公平に思えます。APIは、スクールバスで迷惑な子供と話すのと同じような社会的な感覚をAPIに与えます。あなたは迷惑な状況で立ち往生していますが、あなたはそれに対処することができます。

より「社交的な」アプローチは、移行が最悪の場合無害であることを認め、それを許可することで消費する開発者に親切にすることです。

それで、もしそれが私次第なら、私は例外をスキップします。


15
この答えがあることを思い出させてくれる、時にはそれも簡単な相互作用のための状態遷移をスケッチすることをお勧めします。

6
これを念頭に置いて、例外をスローすることは、つまらなく不公平に思えます。APIは、スクールバスで迷惑な子供と話をするのと同じような社会的感覚をAPIに与えます。>例外はあなたを罰するようには設計されていません:予期しないことが起こったことを伝えるように設計されています。私は個人的に、コードをデバッグするのに何時間も費やす代わりに、地獄が「何も起こらない」(つまり「うまくいかない」)のではないかと考える代わりに投げる方法に非常に感謝します。MediaPlayer.stop()IllegalStateException
誤り言語学者

3
もちろん、ループバック遷移が無効であるとデザインが言っている場合は、はい、例外をスローする必要があります。しかし、私の答えは、その移行が完全にOKである設計についてでした。
メタファイト

1
StopOrThrow()恐ろしいですね。あなたがその路地を下っているなら、なぜ標準パターンを使用しないのTryStop()ですか?また、一般に、APIの動作が不明確な場合(ほとんどがそうであるように)、開発者は単純に推測または実験することを期待されず、ドキュメントを見ることが期待されます。だからこそ、MSDNが大好きです。
メタファイト

1
個人的にはフェイルファーストオプションを好むため、IllegalStateExceptionを使用して、APIのユーザーに、たとえそれが無害であっても、彼のロジックに何か問題があることを示します。私は、多くの場合、本当に私のunfisnihedの仕事はすでに間違っていることを検出するために、参考になっ、自分自身に自分のコードからこれらの例外を取得する
Walfrat

17

実行したいアクションは2種類あります。

  1. 何かがある状態にあることを同時にテストし、別の状態に変更します。

  2. 前の状態に関係なく、何かを特定の状態に設定します。

コンテキストによっては、1つのアクションが必要なものと、他のアクションが必要なものがあります。コンテンツの最後に到達したメディアプレーヤーが「再生」状態のままで、位置が最後にフリーズした場合、「停止」に設定すると同時にプレーヤーが再生状態にあったことをアサートするメソッド状態は、コードが停止要求の前に再生を失敗させないようにする場合に役立ちます(たとえば、メディアをある形式から別の形式に変換する手段として再生中のコンテンツを記録している場合に重要です) 。ただし、ほとんどの場合、重要なのは、操作後、メディアプレーヤーが予期された状態(つまり停止)になっていることです。

メディアの最後に到達したときにメディアプレーヤーが自動的に停止する場合、プレーヤーが実行されていると断言する機能は役に立たないというよりも面倒ですが、場合によっては停止時にプレーヤーが実行されているかどうかを知ることもできます便利である。おそらく、両方のニーズに応える最善の方法は、プレーヤーの前の状態を示す値を関数に返すことです。その状態を考慮するコードは戻り値を調べることができます。気にしないコードは単純に無視できます。


2
古い状態を返すことについてのメモの+1:これにより、操作全体(ステータスのクエリと定義されたステータスへの移行)をアトミックに行う方法で実装できます。これは、少なくとも素晴らしい機能です。奇妙な競合状態が原因で散発的に失敗しない堅牢なユーザーコードを記述しやすくなります。
cmaster-モニカの復元16年

A bool TryStop()void Stop()コード例は、これを本当に優れた答えにします。
ラバーダック

2
@RubberDuck:それは良いパターンではないでしょう。この名前TryStopは、プレーヤーを強制的に停止状態にできない場合、例外をスローしないことを意味します。プレーヤーが既に停止しているときに停止しようとすることは例外的な条件ではありませんが、発信者が関心を持つ可能性がある条件です
。– supercat

1
その後、私はあなたの答えを誤解しました@supercat
ラバーダック

2
APIが再生状態を変更せずにテストする方法を提供する場合、この方法のテストと設定はデメテルの法則に違反しないことを指摘したいと思います。これは、まったく異なる方法である可能性がありますisPlaying()
candied_orange

11

一般的なルールはありません。この特定の場合、APIのユーザーの意図は、プレーヤーによるメディアの再生を停止することです。プレーヤーがメディアを再生していない場合、MediaPlayer.stop()何もしない可能性があり、メソッド呼び出し元の目標はまだ達成されます-メディアは再生されていません。

例外をスローするには、APIのユーザーが、プレーヤーが現在再生中かどうかを確認するか、例外をキャッチして処理する必要があります。これにより、APIの使用が面倒になります。


11

例外の目的は、何か悪いことが起こったことを知らせることではありません。それは合図することです

  1. 何か悪いことが起こった
  2. ここで修正する方法がわからないこと
  3. 呼び出し元、または呼び出しスタックからの何かが対処方法を知っている必要があること
  4. だから私は迅速に救済し、損傷やデータ破損を防ぐために現在のコードパスの実行を停止し、それを呼び出し元に任せてクリーンアップします

呼び出し元が既に停止状態にStopあるものMediaPlayerを試みた場合、これはMediaPlayer解決できない問題ではありません(単純に何もできません)。継続すると、損傷やデータにつながる問題ではありません。何もしないだけで成功する可能性があるため、破損。そして、呼び出し側がを解決できると期待する方が合理的であることは、実際には問題ではありませんMediaPlayer

したがって、この状況では例外をスローしないでください。


+1。これは、ここで例外が適切でない理由を示す最初の回答です。例外は、呼び出し元が例外的なケースについて知る必要がある可能性が高いことを示します。この場合、ほぼ間違いなく、問題のクラスのクライアントは、 'stop()'の呼び出しが実際に状態遷移を引き起こしたかどうかを気にしないので、例外については知りたくないでしょう。
ジュール

5

このように見てください:

プレーヤーが再生していないときにクライアントがStop()を呼び出すと、プレーヤーは現在停止状態にあるため、Stop()は自動的に成功します。


3

ルールは、メソッドの契約に必要なことを行うことです。

このようなstopメソッドに意味のあるコントラクトを定義する複数の方法を見ることができます。stopプレーヤーがプレイしていない場合、メソッドがまったく何もしないことは完全に有効です。その場合、目標によってAPIを定義します。stopped状態に遷移したい場合、stopメソッドはそれを行います- stopped状態からそれ自体にエラーなしで遷移するだけです。

あなたはなぜ何らかの理由があるしたい例外をスローするには?ユーザーコードが最終的に次のようになる場合:

try {
    player.stop();
} catch(IllegalStateException e) {
    // do nothing, player was already stopped
}

その例外が発生してもメリットはありません。質問は、ユーザーがプレーヤーが既に停止しているという事実を気にしますか?彼はそれを照会する別のwaを持っていますか?

プレーヤーは観察可能であり、特定の事柄について既にオブザーバーに通知している場合があります。たとえば、からplayingに遷移するときにオブザーバーに通知stoppingstoppedます。この場合、メソッドに例外をスローさせる必要はありません。stopプレーヤーが既に停止している場合、呼び出しは何も行いません。停止していないときに呼び出すと、遷移についてオブザーバーに通知されます。

全体として、それはあなたのロジックがどこに行き着くかによります。しかし、例外をスローするよりも優れた設計の選択肢があると思います。

呼び出し元が介入する必要がある場合は、例外をスローする必要があります。この場合、彼はそうする必要はありませんが、プレーヤーをそのままにしておくと、そのまま動作し続けます。


3

この決定を下すのに役立つ簡単な一般的な戦略があります。

例外をスローする場合の例外の処理方法を検討してください。

だからあなたの音楽プレーヤーを想像してください、ユーザーが停止をクリックし、再び停止します。そのシナリオでエラーメッセージを表示しますか?私はまだそうするプレーヤーを見ていません。アプリケーションの動作を、「停止」を1回クリックするだけの場合とは異なるものにしたいですか(イベントのログ記録やバックグラウンドでのエラーレポートの送信など)。おそらくない。

つまり、例外をキャッチして、どこかに飲み込む必要があります。そして、それはあなたが別のアクションを取りたくないので、そもそも例外を投げないほうが良いことを意味します。

これは、例外だけでなく、あらゆる種類の分岐に適用されます。基本的に、動作に目に見える違いがないようにする必要がある場合は、分岐する必要はありません。

そうは言っても、停止が「成功」したかどうかを示す列挙値またはenum値stop()を返すようにメソッドを定義しても、それほど害はありませんboolean。おそらくこれを使用することはないでしょうが、戻り値は例外よりも簡単に自然に無視できます。


2

Androidの仕組みは次のとおりです。MediaPlayer

一言で言えば、中に呼び出されていない問題ではないが、停止した状態でのシステムのまま、それでも呼び出すための正当な理由がないので、例外がスローされ、それが再生されているものを知っていないプレーヤーで呼び出された場合そこで止まれ。stopstart


1

ある状況でメソッドが呼び出されることは想定されていないが、その実行がプログラム全般に害を及ぼさない場合の一般的なルールは何ですか?

あなたの答えをあなたに明らかにするかもしれないあなたの声明のマイナーな矛盾であるかもしれないものを見ます。なぜそれがあることであるメソッドが呼び出されることになっていませんし、まだそれの実行は問題はありませんか

メソッドが「呼び出されるべきではない」のはなぜですか?それは自主規制のようです。APIに「害」や影響がない場合、例外はありません。無効または予測不可能な状態を作成する可能性があるため、メソッドを実際に呼び出すべきでない場合は、例外をスローする必要があります。

たとえば、DVDプレーヤーに近づいて「開始」を押す前に「停止」を押してクラッシュした場合、それは意味がありません。ただそこに座っているか、さらに悪い場合には、画面上の「停止」を確認する必要があります。エラーは迷惑であり、決して役に立ちません。

ただし、既にオフになっているときに(メソッドを呼び出して)アラームコードを入れて「オフ」にすることはできますか?その場合は、技術的には「問題は発生しなかった」にもかかわらず、後で問題が発生する可能性があるため、エラーをスローします。実際にその状態にならなかったとしても、これについて知りたいと思います。可能性だけで十分です。これはになりますIllegalStateException

あなたのケースでは、ステートマシンが無効/不必要な呼び出しを処理できる場合は無視し、そうでない場合はエラーをスローします。

編集:

値を検査せずに変数を2回設定しても、コンパイラーはエラーを呼び出しません。また、変数を再初期化することも妨げません。1つの観点から「バグ」と見なされる可能性のあるアクションは数多くありますが、単に「非効率的」、「不要」、「無意味」などです。予想外の結果をもたらさないため、「バグ」。おそらく同様に問題に取り組む必要があります。


それを呼び出すことは、呼び出し元のバグを示すため、呼び出されることになっていない。
user253751

@immibis:必ずしもそうではありません。たとえば、その呼び出し元は、何らかのUIボタンのオンクリックリスナーになる可能性があります。(ユーザーとして、メディアが既に停止している場合に誤って停止ボタンをクリックした場合、エラーメッセージは表示されません。)
メリトン-ストライキで

@meriton UIボタンのオンクリックリスナーである場合、答えは明らかです-「ボタンが押された場合に何をしたいのか」。明らかにそうではなく、アプリケーションの内部的なものです。
user253751

@immibis-返信を編集しました。役に立てば幸いです。
ジム

1

何を正確に行うMediaPlayer.play()MediaPlayer.stop()行う- ?彼らはイベントのユーザー入力のリスナーや、彼らは実際にはシステム上のメディアストリームのいくつかの並べ替えを開始する方法がありますか?すべてがユーザー入力のリスナーである場合、何もしないことは完全に合理的です(ただし、少なくともどこかにログを記録することをお勧めします)。彼らが影響を与える場合は、モデルをあなたのUIは、彼らが投げることができ、制御しているIllegalStateExceptionプレイヤーは、いくつかの並べ替えが必要ですので、isPlaying=trueまたはisPlaying=falseお電話の際は(それがここに書かれてのような実際のブールフラグである必要はない)状態、およびそのMediaPlayer.stop()際にisPlaying=falseMediaPlayerオブジェクトが停止するのに適切な状態にないため、メソッドは実際に「停止」できません-の説明を参照してくださいjava.lang.IllegalStateException クラス:

違法または不適切なときにメソッドが呼び出されたことを通知します。つまり、Java環境またはJavaアプリケーションは、要求された操作に対して適切な状態ではありません。

例外をスローするのが良い理由

多くの人は例外を投げるべきではないので、例外は一般に悪いと考えているようです(ソフトウェアのJoelを参照)。ただし、MediaPlayerGUI.notifyPlayButton()whichがMediaPlayer.play()MediaPlayer.play()呼び出し、他のコードの束を呼び出し、それがPulseAudioなどとインターフェイスする行のどこかにあるとしましょう。

その後、ある日、コードの作業中に「再生」をクリックしても何も起こりません。場合はMediaPlayerGUIController.notifyPlayButton()、ログの何か、少なくとも私は、ログを調べて、ボタンのクリックが実際に登録していたことを確認してください...しかし、なぜそれが再生されないことができますか?さて、実際にMediaPlayer.play()起動するPulseAudioラッパーに何か問題があるとしましょう。「再生」ボタンをもう一度クリックすると、今度は次のメッセージが表示されますIllegalStateException

 Exception in thread "main" java.lang.IllegalStateException: MediaPlayer already in play state.

     at org.superdupermediaplayer.MediaPlayer.play(MediaPlayer.java:16)
     at org.superdupermediaplayer.gui.MediaPlayerGUIController.notifyPlayButton(MediaPlayerGUIController.java:25)
     at org.superdupermediaplayer.gui.MediaPlayerGUI.main(MediaPlayerGUI.java:14)

そのスタックトレースを見ることで、MediaPlayer.play()デバッグする前にすべてを無視し、たとえばオーディオストリームを開始するためにメッセージがPulseAudioに渡されない理由を理解するのに時間を費やすことができます。

一方、例外をスローしない場合、「バカなプログラムは音楽を再生しません」以外に作業を開始することはできません。実際にスローた例外を実際に飲み込むなどの明示的なエラー隠蔽ほど悪くはありません何かがうまくいかないときに他の人の時間を浪費している可能性があります...


0

私は、消費者が間違いを犯すのを困難または不可能にする方法でAPIを設計することを好みます。たとえば、代わりに持つのMediaPlayer.play()MediaPlayer.stop()、あなたが提供することができMediaPlayer.playToggle()、「停止」と「再生」の間で切り替わりいます。このように、メソッドは常に安全に呼び出すことができます-違法な状態になるリスクはありません。

もちろん、これは常に可能または簡単ではありません。指定した例は、リストから既に削除された要素を削除しようとすることに匹敵します。あなたはどちらかです

  • それについて実用的であり、エラーをスローしません。これは確かに多くのコードを単純化します。例えば、if (list.contains(x)) { list.remove(x) }必要なだけを書く必要はありませんlist.remove(x))。しかし、バグを隠すこともできます。
  • または、つまらなくて、リストにその要素が含まれていないことをエラーで知らせることができます。メソッドを呼び出す前に前提条件が満たされているかどうかを常に確認する必要があるため、コードは時々複雑になる可能性がありますが、欠点は最終的なバグの追跡が容易になることです。

MediaPlayer.stop()既に停止しているときに呼び出すことがアプリケーションに害を及ぼさない場合、コードを簡素化し、べき等なメソッドのためのものがあるので、静かに実行させます。しかしMediaPlayer.stop()、これらの条件で呼び出されることは絶対にないと確信している場合、コードのどこかにバグがあり、例外がそれを追跡するのに役立つ可能性があるため、エラーをスローします。


3
playToggle()使いやすくすることに同意しません:目的の効果(プレーヤーの演奏または演奏なし)を達成するには、その現在の状態を知る必要があります。そのため、プレーヤーの停止を要求するユーザー入力を取得した場合、まずプレーヤーが現在再生中かどうかを問い合わせてから、答えに応じて状態を切り替える必要があります。これは、stop()メソッドを呼び出してそれを実行するよりもはるかに面倒です。
cmaster-モニカの復元16年

まあ、私はそれが使いやすいと言ったことはありません-私の主張はそれがより安全だということでした。さらに、この例では、ユーザーに個別の停止ボタンと再生ボタンが与えられることを想定しています。それが実際にそうであれば、はい、使用するのplayToggleは非常に面倒です。しかし、代わりにユーザーにトグルボタンも与えられたplayToggle場合、再生/停止よりも使いやすいでしょう。
ペドロロドリゲス
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.