假设我有一个MediaPlayer类,该类具有play()和stop()方法。如果以前没有调用过play方法,则在实现stop方法时使用的最佳策略是什么。我看到两个选项:由于播放器未处于适当状态而引发异常,或无提示地忽略对stop方法的调用。
当在某些情况下不应该调用某个方法但该方法的执行对程序总体无害时,一般规则应该是什么?
假设我有一个MediaPlayer类,该类具有play()和stop()方法。如果以前没有调用过play方法,则在实现stop方法时使用的最佳策略是什么。我看到两个选项:由于播放器未处于适当状态而引发异常,或无提示地忽略对stop方法的调用。
当在某些情况下不应该调用某个方法但该方法的执行对程序总体无害时,一般规则应该是什么?
Answers:
没有规则。这完全取决于您要如何使自己的API感到“满意”。
就个人而言,我认为在音乐播放器中,从状态Stopped
到Stopped
方法的转换Stop()
是完全有效的状态转换。它不是很有意义,但是是有效的。考虑到这一点,抛出异常似乎是ped脚和不公平的。这将使API感觉就像在校车上与讨厌的孩子聊天一样具有社交感。您对烦人的情况感到困惑,但是可以解决。
一种更“善于交际”的方法是承认这种过渡在最坏的情况下是无害的,并且通过允许它对使用中的开发人员友好。
因此,如果由我决定,我将跳过该异常。
MediaPlayer.stop()
方法,IllegalStateException
而不是花费数小时来调试代码,并且想知道为什么地狱“什么也没有发生”(即“它根本不起作用”)。
StopOrThrow()
听起来很恐怖。如果您要沿着那条小巷走,为什么不使用标准模式TryStop()
?同样,通常,当API的行为不清楚(大多数情况下)时,不希望开发人员只是简单地猜测或尝试,就应该让他们查看文档。这就是为什么我如此喜欢MSDN。
一个人可能希望执行两种不同的动作:
同时测试某事物处于一种状态,然后将其更改为另一种状态。
将某些内容设置为特定状态,而不考虑先前的状态。
有些情况需要一个动作,而另一些情况则需要另一种动作。如果到达内容结尾的媒体播放器将保持“播放”状态,但位置冻结在结尾,则该方法会同时断言播放器处于播放状态,同时将其设置为“停止”如果代码要确保在停止请求之前没有导致回放失败的状态,则state可能有用(如果例如正在录制正在回放的内容,以将媒体从一种格式转换为另一种格式,则这可能很重要) 。但是,在大多数情况下,重要的是在操作之后,媒体播放器处于预期状态(即停止)。
如果媒体播放器到达媒体末尾时会自动停止播放,则断言播放器正在运行的功能可能会比帮助有用,但是可能在某些情况下知道播放器在停止时是否正在运行有用。满足这两种需求的最佳方法可能是让函数返回一个指示玩家以前状态的值。关心那个状态的代码可以检查返回值;无关紧要的代码可以简单地忽略它。
bool TryStop()
和void Stop()
代码示例将使这是一个真正优秀的答案。
TryStop
表示如果不能强迫播放器进入停止状态,则不应引发异常。试图在播放器已经停止时停止播放器并不是一个例外情况,而是呼叫者可能感兴趣的条件
isPlaying()
。
例外的目的不是要预示发生了什么坏事。表示
如果调用者试图Stop
一个MediaPlayer
一个已经处于停止状态,这不是一个问题,即MediaPlayer
无法解析(也可以干脆什么也不做。)这不是一个问题,如果继续下去,会导致数据损坏或腐败,因为无所事事就可以成功。实际上,期望调用者能够解决比调用者更合理的问题并不是真正的问题MediaPlayer
。
因此,在这种情况下,您不应抛出异常。
规则是您执行方法合同所要求的。
我可以看到为这种stop
方法定义有意义的合同的多种方法。stop
如果播放器不播放,则该方法完全不执行任何操作可能是完全有效的。在这种情况下,您可以根据其目标定义API。您想要过渡到stopped
状态,该stop
方法就可以做到这一点-只需从stopped
状态无错误地过渡到自身即可。
有什么理由要引发异常吗?如果用户代码最后看起来像这样:
try {
player.stop();
} catch(IllegalStateException e) {
// do nothing, player was already stopped
}
那就没有例外了。所以问题是,用户是否会担心播放器已经停止的事实?他还有另一个问题要查询吗?
玩家可能会观察到的,并且可能已经通知对某些事情的观察员-当它从transitons如通知观察者playing
来stopping
来stopped
。在这种情况下,不必使方法引发异常。stop
如果播放器已经停止,调用将不执行任何操作,而在播放器未停止时调用将通知观察者有关过渡的信息。
总而言之,这取决于逻辑的最终结果。但是我认为有更好的设计选择,然后引发例外。
如果调用者必须干预,则应抛出异常。在这种情况下,他不必这样做,您可以简单地让播放器保持原样,然后继续工作。
有一个简单的一般策略可以帮助您做出此决定。
考虑如果要抛出异常,将如何处理该异常。
因此,想象一下您的音乐播放器,用户单击“停止”,然后再次停止。您要在这种情况下显示错误消息吗?我还没有看过这样的球员。您是否希望应用程序行为与仅单击一次停止(例如记录事件或在后台发送错误报告)有什么不同?可能不会。
这意味着需要在某个地方捕获并吞下异常。这意味着您最好不要首先抛出异常,因为您不想采取不同的行动。
这不仅适用于异常,而且适用于任何种类的分支:基本上,如果行为上不需要有可观察的差异,就不需要分支。
就是说,定义您的stop()
方法返回一个boolean
或一个枚举值指示停止是否“成功” 并没有多大危害。您可能永远不会使用它,但是与异常相比,返回值更容易自然地被忽略。
这是android的工作方式:MediaPlayer
简而言之,stop
当start
没有被调用不是问题时,系统会保持停止状态,但是如果在甚至不知道正在播放什么的播放器上调用,则会引发异常,因为没有充分的理由调用停在那里
当在某些情况下不应该调用某个方法但该方法的执行对程序总体无害时,一般规则应该是什么?
我认为您的陈述中可能有一个小矛盾,可能会使您明白答案。为什么不应该调用该方法而执行该方法却无害呢?
为什么该方法“不应该被调用”?这似乎是一个自我施加的限制。如果对您的API没有任何“危害”或影响,那么也不例外。如果确实不应该调用该方法,因为它可能会创建无效或不可预测的状态,则应引发异常。
例如,如果我走到DVD播放机上并在单击“开始”之前先按“停止”,然后它崩溃了,那对我来说就没有意义了。它应该坐在那里,或者更糟的是,在屏幕上确认“停止”。错误会很烦人,对您毫无帮助。
但是,如果已经关闭(调用该方法),我是否可以输入一个警报代码将其“关闭”,这可能会导致它进入一种状态,从而阻止以后对其进行适当的布防?如果是这样,则即使从技术上讲“没有造成任何伤害”,也将引发错误,因为以后可能会出现问题。我想知道这一点,即使它实际上并没有进入那种状态。只是可能性就足够了。这将是一个IllegalStateException
。
在您的情况下,如果您的状态机可以处理无效/不必要的调用,请忽略它,否则抛出错误。
编辑:
请注意,如果您两次设置变量而不检查值,则编译器不会调用错误。这也不会阻止您重新初始化变量。从一个角度看,有很多动作可以被认为是“错误”,而仅仅是“低效”,“不必要”,“毫无意义”等。我试图在我的回答中提供这种观点-通常不考虑做这些事情一个“错误”,因为它不会导致任何意外情况。您可能应该同样处理您的问题。
究竟是做什么MediaPlayer.play()
和MediaPlayer.stop()
做什么?-它们是事件监听器以供用户输入,还是实际上是在系统上启动某种媒体流的方法?如果它们都是用户输入的侦听器,那么他们什么也不做是完全合理的(尽管至少将它们记录在某个地方是个好主意)。但是,如果它们影响模型的UI被控制,那么他们可能会引发IllegalStateException
因为球员应该有某种isPlaying=true
或isPlaying=false
状态(它不一定是一个实际的布尔标志喜欢这里写的),所以当你打电话MediaPlayer.stop()
时isPlaying=false
,该方法实际上无法“停止”,MediaPlayer
因为对象未处于要停止的适当状态-请参见java.lang.IllegalStateException
类:
表示已在非法或不适当的时间调用了方法。换句话说,对于所请求的操作,Java环境或Java应用程序没有处于适当的状态。
许多人似乎认为,一般来说,异常是不好的,因为它们永远都不会抛出(参见Joel on Software)。但是,假设您有一个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以启动音频流。
另一方面,如果您不抛出异常,那么除了“愚蠢的程序不会播放任何音乐”之外,我将无事可做。虽然不是那么糟糕,因为明确的错误隐藏,如实际吞咽其中一个例外是在事实上抛出,你仍然有可能浪费别人的时候确实存在错误的时间......所以是很好,在他们抛出异常。
我更喜欢以一种使消费者更难或不可能出错的方式设计API。例如,您可以提供在“已停止”和“正在播放”之间切换的功能,而不必使用MediaPlayer.play()
和。这样,该方法始终可以安全调用-不会进入非法状态。MediaPlayer.stop()
MediaPlayer.playToggle()
当然,这并非总是可能或容易做到的。您提供的示例类似于尝试删除已从列表中删除的元素。你可以
if (list.contains(x)) { list.remove(x) }
您不必只需要编写代码list.remove(x)
。但是,它也可以隐藏错误。如果MediaPlayer.stop()
在它停止后调用不会对您的应用程序造成伤害,那么我会让它静默运行,因为它简化了代码,并且我有幂等方法。但是,如果您绝对确定MediaPlayer.stop()
在那种情况下永远不会调用它,那么我会抛出一个错误,因为代码中的其他地方可能存在错误,而异常会帮助您找到错误。
playToggle()
使用起来更简单:要达到理想的效果(播放器播放还是不播放),您需要了解其当前状态。因此,如果收到要求停止播放器的用户输入,则首先需要查询播放器当前是否正在播放,然后根据答案切换其状态。这比仅调用一个stop()
方法并完成它要麻烦得多。
playToggle
使用a 将非常麻烦。但是,如果还给用户提供了一个切换按钮,则playToggle
它将比播放/停止更容易使用。