Hangisi daha iyi? IllegalStateException veya sessiz yöntem yürütme? [kapalı]


16

Diyelim ki play () ve stop () yöntemlerine sahip bir MediaPlayer sınıfım var. Play yönteminin daha önce çağrılmamış olması durumunda stop yöntemini uygularken kullanılacak en iyi strateji hangisidir? İki seçenek görüyorum: oynatıcı uygun durumda olmadığından bir istisna atın veya durdurma yöntemine yapılan çağrıları sessizce yok sayın.

Bir yöntemin bazı durumlarda çağrılmaması gerekiyorsa, ancak yürütülmesi genel olarak programa zarar vermediğinde genel kural ne olmalıdır?


21
İstisnai olmayan davranışları modellemek için istisnalar kullanmayın.
Alexander - Monica'yı eski

1
Hey! Bunun bir kopya olduğunu sanmıyorum. Sorum IllegalStateException hakkında daha fazla ve yukarıdaki soru genel olarak istisnaları kullanma hakkında.
x2bool

1
SADECE sessizce başarısız olmak yerine, neden anormalliği bir uyarı olarak kaydetmiyorsunuz? Şüphesiz, kullandığınız Java farklı günlük düzeylerini (HATA, UYARI, BİLGİ, HATA AYIKLAMA) destekler.
Eric Seastrand

3
Öğe kümede bulunuyorsa Set.add öğesinin bir istisna atmadığına dikkat edin. Amacı, "öğeyi kümeye eklemek" yerine "öğenin kümede olduğundan emin olmak" tır, böylece öğe kümede zaten varsa hiçbir şey yapmadan amacına ulaşır.
user253751

2
Günün sözcüğü: idempotence
Jules

Yanıtlar:


37

Kural yok. Tamamen API'nızı nasıl "hissetmek" istediğinize bağlıdır.

Şahsen, bir müzik çalar, ben devletten bir geçiş düşünüyorum Stoppediçin Stoppedyöntemi ile Stop()mükemmel geçerli durum geçiş olduğunu. Çok anlamlı değil, ama geçerli. Bunu akılda tutarak, bir istisna atmak bilgiçlik ve haksızlık gibi görünebilir. API'yı, okul otobüsündeki sinir bozucu çocukla konuşmanın sosyal eşdeğeri gibi hissettirecekti. Sinir bozucu durumla sıkışıp kaldınız, ancak bununla başa çıkabilirsiniz.

Daha "sosyal" bir yaklaşım, geçişin en kötü zararsız olduğunu kabul etmek ve izin vererek tüketen geliştiricilere karşı nazik olmaktır.

Yani, bana kalmış olsaydı, istisnayı atlardım.


15
Bu cevap bize bazen basit etkileşimler için bile devlet geçişlerini çizmenin iyi bir fikir olduğunu hatırlatır .

6
Bunu akılda tutarak, bir istisna atmak bilgiçlik ve haksızlık gibi görünebilir. API'yı, okul otobüsündeki sinir bozucu çocukla konuşmanın sosyal eşdeğeri gibi hissettirecekti. > İstisnalar sizi cezalandırmak için tasarlanmamıştır: Size beklenmedik bir şey olduğunu söylemek için tasarlanmıştır. Ben saatlerce hata ayıklama kodu harcamak ve neden cehennem "hiçbir şey" (yani "sadece çalışmıyor") merak yerine attı bir yöntem için çok minnettar olacaktır . MediaPlayer.stop()IllegalStateException
errantlinguist

3
Tabii ki, tasarımınız geri döngü geçişinin geçersiz olduğunu söylüyorsa, EVET bir istisna atmalısınız. Ancak cevabım, bu geçişin mükemmel olduğu bir tasarımla ilgiliydi.
MetaFight

1
StopOrThrow()kulağa korkunç geliyor. Eğer o caddeden aşağı gidiyorsanız neden standart kalıbını kullanmıyorsunuz TryStop()? Ayrıca, genellikle, bir API'nın davranışı belirsiz olduğunda (çoğu gibi) geliştiricilerin basitçe tahmin etmeleri veya deney yapmaları beklenmez, dokümantasyona bakmaları beklenir. Bu yüzden MSDN'i çok seviyorum.
MetaFight

1
Şahsen ben başarısız-hızlı seçeneği tercih, bu yüzden zararsız olsa bile API kullanıcısına mantık yanlış bir şey olduğunu belirtmek için IllegalStateException için gitmek istiyorum. Sık sık kendi kodumdan bu istisnaları alıyorum, unfisnihed işimin zaten yanlış olduğunu tespit etmeye gerçekten yardımcı oluyor
Walfrat

17

Birinin gerçekleştirmek isteyebileceği iki farklı eylem türü vardır:

  1. Aynı anda bir şeyin bir durumda olduğunu test edin ve başka bir durumda değiştirin.

  2. Önceki duruma bakmadan belirli bir duruma bir şey ayarlayın.

Bazı bağlamlar bir eylem, bazıları da diğerini gerektirir. İçeriğin sonuna ulaşan bir medya oynatıcı "oynatma" durumunda kalacak, ancak pozisyon sonunda donmuş olacaksa, oynatıcının "durdurma" olarak ayarlanırken aynı anda oynatma durumunda olduğunu söyleyen bir yöntem , kod, durdurma isteğinden önce oynatmanın başarısız olmasına neden olan hiçbir şeyin yapılmamasını sağlamak istiyorsa yararlı olabilir (örneğin, bir şey medyayı bir biçimden diğerine dönüştürmenin bir yolu olarak oynatılan içeriği kaydediyorsa önemli olabilir) . Bununla birlikte, çoğu durumda, önemli olan işlemden sonra medya yürütücünün beklenen durumda (yani durdurulmuş) olmasıdır.

Bir medya yürütücü medyanın sonuna geldiğinde otomatik olarak durursa, oynatıcının çalıştığını iddia eden bir işlev büyük olasılıkla yardımcı olmaktan daha can sıkıcı olabilir, ancak bazı durumlarda oynatıcının durdurulduğu sırada çalışıp çalışmadığını bilmek, Bir işe yara. Belki de her iki ihtiyacı da karşılamak için en iyi yaklaşım, oyuncunun önceki durumunu gösteren bir değer döndürmesini sağlamaktır. Bu duruma önem veren kod, dönüş değerini inceleyebilir; umursamayan kod onu görmezden gelebilir.


2
Eski durumu döndürmeyle ilgili not için +1: Bu, tüm işlemi (durumu sorgulama ve tanımlı bir duruma geçme) atomik hale getirecek şekilde uygulanmasına izin verir, ki bu en azından hoş bir özelliktir. Bazı garip yarış durumları nedeniyle ara sıra başarısız olmayan sağlam kullanıcı kodunun yazılmasını kolaylaştırır.
cmaster - monica

A bool TryStop()ve void Stop()kod örneği bunu gerçekten mükemmel bir cevap haline getirecektir.
RubberDuck

2
@ RubberDuck: Bu iyi bir model olmazdı. İsim TryStop, oyuncu durdurulmuş bir duruma zorlanamazsa bir istisna atılmaması gerektiği anlamına gelir. Zaten durduğunda oyuncuyu durdurmaya çalışmak istisnai bir durum değildir, ancak arayanın ilgilendiği bir durumdur.
supercat

1
Sonra cevabını yanlış anladım @supercat
RubberDuck

2
API'nin ayrıca oynatma durumunu değiştirmeden test etmenin bir yolunu sağlaması şartıyla, bu şekilde test etmenin ve ayarlamanın demeter yasasının ihlali olmadığını belirtmek isterim. Bu tamamen farklı bir yöntem olabilir isPlaying().
candied_orange

11

Genel bir kural yoktur. Bu özel durumda, API'nızın kullanıcısının amacı oynatıcının medyayı oynatmasını durdurmaktır. Oynatıcı medyayı oynatmıyorsa, MediaPlayer.stop()hiçbir şey yapamaz ve yöntem arayanın amacına hala ulaşılacaktır - medya oynatılmıyor.

Bir istisna atmak, API kullanıcısının oynatıcının oynatılıp oynatılmadığını kontrol etmesini veya istisnayı yakalayıp işlemesini gerektirir. Bu, API'yi daha çok bir angarya haline getirir.


11

İstisnaların amacı kötü bir şey olduğuna işaret etmek değildir; işaret etmek

  1. Kötü bir şey oldu
  2. burada nasıl düzeltileceğini bilmiyorum
  3. arayanın veya ondan gelen çağrı saldırısının nasıl yapılacağını bilmesi
  4. bu yüzden hızlı bir şekilde kurtarıyorum, hasar veya veri bozulmasını önlemek için mevcut kod yolunun yürütülmesini durduruyorum ve temizlemek için arayana bırakıyorum

Arayan zaten durdurulmuş durumda olan Stopbir MediaPlayerşeye çalışırsa , bu çözülemeyen bir sorun MediaPlayerdeğildir (basitçe hiçbir şey yapamaz.) Devam ederse hasara veya verilere yol açacak bir sorun değildir çünkü hiçbir şey yapmadan başarılı olabilir. Ve gerçekten arayan bir sorunu çözmek mümkün beklemek daha makul bir sorun değildir MediaPlayer.

Bu nedenle, bu durumda bir istisna atmamalısınız.


+1. Burada bir istisnanın neden uygun olmadığını gösteren ilk cevap budur . İstisnalar, arayanın istisnai durum hakkında bilgi sahibi olması gerektiğini gösterir. Bu durumda, neredeyse kesinlikle söz konusu sınıfın istemcisi 'stop ()' çağrısının gerçekte bir durum geçişine neden olup olmadığını umursamayacaktır, bu nedenle bir istisna hakkında bilmek istemeyecektir.
Jules

5

Şuna şöyle bak:

İstemci, oynatıcı çalmadığında Stop () öğesini çağırırsa, oynatıcı şu anda durdurulmuş durumda olduğu için Stop () otomatik olarak başarılı olur.


3

Kural, yönteminizin sözleşmesinin gerektirdiği şeyi yapmanızdır.

Böyle bir stopyöntem için anlamlı sözleşmeler tanımlamanın birçok yolunu görebiliyorum . stopOyuncu oynamıyorsa yöntemin kesinlikle hiçbir şey yapmaması mükemmel bir şekilde geçerli olabilir . Bu durumda API'nızı hedeflerine göre tanımlarsınız. stoppedDevlete geçmek istiyorsunuz ve stopyöntem bunu yapıyor - sadece stoppeddevletten hatasız olarak kendi içine geçerek.

İstisna atmak istemenizin bir nedeni var mı ? Sonunda kullanıcı kodu şöyle görünecekse:

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

bu istisnaya sahip olmanın hiçbir yararı yoktur. Yani soru şu ki, kullanıcı oynatıcının zaten durdurulmuş olması gerçeğine önem verecek mi? Sorgulamak için başka bir wa var mı?

Oyuncu gözlemlenebilir olabilir ve zaten belli şeyleri gözlemci haberdar olabilir - bu gelen transitons zaman mesela bildirimde bulunan gözlemci playingiçin stoppingiçin stopped. Bu durumda, yöntemin bir istisna atması gerekli değildir. stopOyuncu zaten durdurulmuşsa aramak hiçbir şey yapmaz ve durdurulmadığında aramak gözlemcileri geçiş hakkında bilgilendirir.

Sonuçta, mantığınızın nerede biteceğine bağlıdır. Ama bence bir istisna yaratan daha iyi tasarım seçenekleri var.

Arayanın müdahale etmesi gerekiyorsa bir istisna atmalısınız . Bu durumda, oyuncunun olduğu gibi olmasına izin verebilirsiniz ve çalışmaya devam eder.


3

Bu kararı vermenize yardımcı olacak basit bir genel strateji vardır.

Atılırsa istisnanın nasıl ele alınacağını düşünün.

Müzik çalarınızı düşünün, kullanıcı durup tekrar durur'u tıklar. Bu senaryoda bir hata mesajı görüntülemek ister misiniz? Henüz bir oyuncu görmedim. Uygulama davranışının yalnızca bir kez durdur'u tıklatmaktan farklı olmasını mı istiyorsunuz (etkinliği günlüğe kaydetme veya arka planda bir hata raporu gönderme gibi)? Muhtemelen değil.

Bu, istisnanın bir yere yakalanması ve yutulması gerektiği anlamına gelir. Ve bu, ilk etapta istisnayı atmamanızdan daha iyi olduğunuz anlamına gelir, çünkü farklı bir işlem yapmak istemezsiniz .

Bu sadece istisnalar için değil, her türlü dallanma için de geçerlidir: temel olarak davranışta gözlemlenebilir bir fark olması gerekmiyorsa, dallamaya gerek yoktur.

Bununla birlikte, stop()bir booleanveya enum değeri döndürme yönteminizi tanımlamanın, durdurmanın "başarılı" olup olmadığını belirten çok fazla zararı yoktur . Muhtemelen hiç kullanmayacaksınız, ancak bir dönüş değeri bir istisnadan daha kolay ve doğal olarak göz ardı edilebilir.


2

Android bunu nasıl yapıyor: MediaPlayer

Özetle, stopne zaman startdenilen edilmemiş bir sorun bile ne çaldığını bilmiyor bir oyuncu çağırdı eğer arama için iyi bir neden olmadığı için, durmuş halde sistem kalır, ancak, bir istisna atılmaz değil orda dur.


1

Bir yöntemin bazı durumlarda çağrılmaması gerekiyorsa, ancak yürütülmesi genel olarak programa zarar vermediğinde genel kural ne olmalıdır?

İfadenizde cevabı size netleştirebilecek küçük bir çelişki olabileceğini görüyorum. Neden yöntemin çağrılmaması gerekiyor ama yine de yürütülmesi zarar vermiyor ?

Neden "çağrılmaması gerekiyor" yöntemi? Bu kendi kendine dayatılan bir kısıtlama gibi görünüyor. API'nıza "zarar" veya etki yoksa, bir istisna yoktur. Yöntemin geçersiz veya öngörülemeyen durumlar oluşturabileceği için gerçekten çağrılmaması gerekiyorsa, bir istisna atılmalıdır.

Örneğin, bir DVD oynatıcıya doğru yürüdüm ve "start" tuşuna basmadan önce "stop" a basarsam ve çökerse, bu bana mantıklı gelmez. Sadece orada oturmalı veya daha da kötüsü, ekranda "dur" u kabul etmelidir. Bir hata can sıkıcı olabilir ve hiçbir şekilde yardımcı olmaz.

Ancak, zaten kapalı olduğunda (yöntemi çağırarak) "kapatmak" için bir alarm kodu koyabilir miyim, bu daha sonra düzgün silahlanma engelleyecek bir duruma girmek neden olabilir? Eğer öyleyse, daha sonra bir sorun olabileceğinden, teknik olarak "herhangi bir zarar gelmedi" rağmen bir hata atın. Aslında o duruma girmemiş olsa bile bunu bilmek isterdim. Sadece olasılık yeter. Bu bir olurdu IllegalStateException.

Durumunuzda, durum makineniz geçersiz / gereksiz aramayı işleyebiliyorsa, yok sayın, aksi takdirde bir hata atın.

DÜZENLE:

Değeri denetlemeden bir değişkeni iki kez ayarlarsanız, bir derleyicinin hata vermediğini unutmayın. Ayrıca bir değişkeni yeniden başlatmanızı engellemez. Bir açıdan "hata" olarak kabul edilebilecek, ancak sadece "verimsiz", "gereksiz", "anlamsız" gibi birçok eylem vardır. Cevabımda bu perspektifi sağlamaya çalıştım - bu şeyleri yapmak genellikle dikkate alınmaz bir "hata" çünkü beklenmedik bir şeyle sonuçlanmaz. Muhtemelen probleminize benzer şekilde yaklaşmalısınız.


Çağrılması gerekmez, çünkü çağırmak arayanda bir hata olduğunu gösterir.
user253751

@immibis: Mutlaka değil. Örneğin, bu arayan bazı UI düğmelerinin tıklama üzerine dinleyicisi olabilir. (Kullanıcı olarak, medya zaten durdurulmuşsa yanlışlıkla durdur düğmesini tıklarsanız muhtemelen bir hata mesajı
istemezsiniz

@meriton Bir kullanıcı arayüzü düğmesinin tıklama dinleyicisi olsaydı, cevap açık olurdu - "düğmeye basılırsa ne yapmak istiyorsanız onu yapın". Açıkçası değil, uygulama içinde bir şey.
user253751

@immibis - Yanıtımı düzenledim. Umut ediyorum bu yardım eder.
Jim

1

Tam olarak ne yapar MediaPlayer.play()ve MediaPlayer.stop()yaparlar? - kullanıcı girişi için olay dinleyicileri mi yoksa sistemde bir çeşit medya akışını başlatan yöntemler mi? Eğer hepsi kullanıcı girişi için dinleyici ise, o zaman hiçbir şey yapmamaları mantıklıdır (en azından bir yerde oturum açmak iyi bir fikir olacaktır). Onlar etkileyen Ancak, modeli UI kontrol ediyor, sonra da bir atabilseydin IllegalStateExceptionoyuncu bazı tür olmalıdır, çünkü isPlaying=trueya isPlaying=falsedevlet (burada yazılı gibi gerçek bir Boole bayrağı olması gerekmez) ve böylece Aradığınızda MediaPlayer.stop()zaman isPlaying=false, yöntemi aslında "durduramaz" MediaPlayerçünkü nesne durdurulacak uygun durumda değildir -java.lang.IllegalStateException sınıf:

Bir yöntemin yasa dışı veya uygunsuz bir zamanda çağrıldığını gösterir. Başka bir deyişle, Java ortamı veya Java uygulaması istenen işlem için uygun durumda değil.

İstisnaları atmak neden iyi olabilir?

Birçok insan, istisnaların genel olarak kötü olduğunu düşünüyor gibi görünüyor, çünkü asla atılmamalıdır (bkz. Joel on Software ). Bununla birlikte, bir MediaPlayerGUI.notifyPlayButton()çağrıya sahip olduğunuzu MediaPlayer.play()ve MediaPlayer.play()diğer kodların bir demetini ve örneğin PulseAudio ile arayüz oluşturduğu çizginin bir kısmını çağırdığını varsayalım , ancak ben (geliştirici) nerede bilmiyorum çünkü tüm bu kodu yazmadım.

Sonra, bir gün kod üzerinde çalışırken, "play" i tıklıyorum ve hiçbir şey olmuyor. Bir MediaPlayerGUIController.notifyPlayButton()şey kaydederse, en azından günlüklere bakabilir ve düğme tıklamasının gerçekten kayıtlı olup olmadığını kontrol edebilirim ... ama neden oynamıyor? Peki, aslında çağrı yapan PulseAudio sarmalayıcılarında bir sorun var diyelim MediaPlayer.play(). Tekrar "oynat" düğmesini tıklıyorum ve bu sefer şunu alıyorum 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)

Bu yığın izlemesine bakarak, MediaPlayer.play()hata ayıklamadan önce her şeyi göz ardı edebilir ve zamanımı örneğin ses akışı başlatmak için PulseAudio'ya hiçbir mesajın geçmediğini anlayarak harcayabilirim.

Eğer Öte yandan, yok bir istisna, ben başka ile çalışmaya başlamak için hiçbir şey gerekir: "aptal programı herhangi bir müzik çalmıyor"; Her ne kadar değil açık kadar kötü hata gizleme gibi aslında istisna yutma oldu aslında atılmış , yine potansiyel olarak bir şey go yanlış yapar başkalarının zamanını böyle mi ... öylesine güzel ve onlara bir özel durum.


0

API'yı, tüketicinin hata yapmasını zorlaştıracak veya imkansız hale getirecek şekilde tasarlamayı tercih ediyorum. Örneğin, sahip yerine MediaPlayer.play()ve MediaPlayer.stop(), sen sağlayabilir MediaPlayer.playToggle()"durdu" ve "oynamak" arasında hangi geçiş yapar. Bu şekilde, yöntemi aramak her zaman güvenlidir - yasadışı bir duruma girme riski yoktur.

Tabii ki, bu her zaman mümkün veya kolay değildir. Verdiğiniz örnek, önceden listeden kaldırılmış bir öğeyi kaldırmaya çalışmakla karşılaştırılabilir. Ya sen

  • pragmatik ve herhangi bir hata atmayın. Bu kesinlikle çok sayıda kodu basitleştirir, örneğin, if (list.contains(x)) { list.remove(x) }sadece ihtiyacınız olanı yazmak yerine list.remove(x)). Ancak, hataları da gizleyebilir.
  • ya da bilgiç olabilirsiniz ve listenin bu öğeyi içermediğine dair bir hata sinyali alabilirsiniz. Yöntemi çağırmadan önce ön koşulların yerine getirilip getirilmediğini sürekli olarak doğrulamanız gerektiğinden, kod zaman zaman daha karmaşık hale gelebilir, ancak üst kısım, nihai hataların izlenmesi daha kolaydır.

MediaPlayer.stop()Zaten durdurulduğunda çağrının uygulamanızda bir zararı yoksa, kodu basitleştirdiği ve idempotent yöntemler için bir şeyim olduğu için sessiz çalışmasına izin verirdim. Ancak MediaPlayer.stop()bu koşullarda asla çağrılmayacağından kesinlikle eminseniz , bir hata atarım çünkü kodda başka bir yerde bir hata olabilir ve istisna bunu izlemeye yardımcı olacaktır.


3
playToggle()Kullanımı daha kolay olmama katılmıyorum : İstenen efekti elde etmek için (oyuncu oynuyor veya oynamıyor), mevcut durumunu bilmeniz gerekir. Bu nedenle, oynatıcıyı durdurmanızı isteyen kullanıcı girişi alırsanız, önce oynatıcının oynatılıp oynatılmadığını sormanız, ardından cevaba bağlı olarak durumunu değiştirmeniz veya değiştirmemeniz gerekir. Bu sadece bir stop()yöntem çağırmak ve onunla yapılmaktan çok daha hantaldır .
cmaster - monica

Asla kullanmanın daha kolay olduğunu söylemedim - iddiam daha güvenli olduğu yönündeydi. Ayrıca, örneğin, kullanıcıya ayrı durdurma ve oynatma düğmeleri verileceğini varsayar. Eğer gerçekten durum böyle playToggleolsaydı , evet, kullanmak çok hantal olurdu. Ancak kullanıcıya bunun yerine bir geçiş düğmesi de verilirse, playToggleoynat / durdurdan daha kolay kullanılır.
Pedro Rodrigues
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.