Kötü stil / tehlikeli dönüş sonrası iş yapmak için nihayet yan tümce kullanımı mı?


30

Bir Yineleyici yazmanın bir parçası olarak kendimi aşağıdaki kod parçasını yazarken buldum (hata sıyırma)

public T next() {
  try {
    return next;
  } finally {
    next = fetcher.fetchNext(next);
  }
}

okumaktan biraz daha kolay bulmak

public T next() {
  T tmp = next;
  next = fetcher.fetchNext(next);
  return tmp;
}

Okunabilirlik farkının çok zor olamayacağı basit bir örnek olduğunu biliyorum, ancak istisnaların olmadığı durumlarda böyle bir durumda try-nihayet kullanmanın kötü olup olmadığı konusunda genel görüşe ilgi duyuyorum. kodu basitleştirdiğinde aslında tercih edilirse.

Eğer kötüyse: neden? Stil, performans, tuzaklar, ...?

Sonuç Tüm cevaplarınız için teşekkür ederiz! Sanırım sonuç (en azından benim için), ilk örneğin, ortak bir model olsaydı daha okunaklı olabileceği, ancak olmadığı anlamına gelir. Bu nedenle, amacı dışında bir yapı kullanarak ortaya çıkan karışıklık, muhtemelen gizlenmiş istisna akışı ile birlikte, herhangi bir basitleştirmeden daha ağır basacaktır.


10
Şahsen ikincisini ilkinden daha çok anlayabilecektim ama nadiren finallyblok kullanıyorum.
Christian Mann

2
Geçerli akımı döndür = fetcher.fetchNext (geçerli); // Buna ne dersin, aka gerçekten ön taramaya ihtiyacın var mı?
eşarp

1
@scarfridge Örnek, çalışmak Iteratoriçin bir çeşit ön taramaya ihtiyaç duyduğunuz yerde uygulama yapmakla ilgilidir hasNext(). Kendin dene.
maaartinus

1
@maaartinus Bunun farkındayım. Bazen hasNext () basitçe doğru döndürür, örneğin dolu taş dizisi ve bazen bir sonraki öğeyi almak oldukça pahalıdır. İkinci durumda, yalnızca tembel başlatmaya benzer şekilde talep üzerine öğeyi almaya başvurmalısınız.
eşarp

1
@scarfridge Yaygın kullanımıyla ilgili sorun Iterator, değeri almanız gerektiğidir hasNext()('bunu almak oldukça sık olup olmadığını öğrenmek için tek yoldur) ve next()OP'nin yaptığı gibi geri döndürmek .
maaartinus

Yanıtlar:


18

Şahsen, komut sorgusu ayırma fikri üzerine bir fikir verirdim . Hemen aşağı indiğinizde, next () iki amacı vardır: bir sonraki öğeyi almak için ilan edilen amaç ve bir sonraki iç durumunu değiştirmenin gizli yan etkisi. Öyleyse, yaptığınız şey, yöntemin gövdesinde reklamı yapılan amaca ulaşmak ve sonra nihayet bir maddede gizli bir yan etkiye dokunmak, ki bu tam olarak 'yanlış olmasa da ... garip görünüyor.

Gerçekten kaynaştıran şey, kodun ne kadar anlaşılır olduğu, diyelim ve bu durumda cevap "meh" dir. Basit bir return ifadesini "deniyorsunuz" ve sonra güvenli olmayan hata kurtarma için olması gereken bir kod bloğunda gizli bir yan etki yürütüyorsunuz. Yaptığınız şey 'zeki' ve 'zeki' kod genellikle bakıcıları mırıltılamaya neden olur, “ne… oh sanırım anlıyorum”. Kodunuzu okuyan insanlar için mırıldanıyor "evet, ah-ha, mantıklı, evet ..."

Peki, devlet mutasyonunu erişimci çağrılarından ayırırsanız ne olur? İlgilendiğiniz okunabilirlik konusunun tartışmasız hale geldiğini hayal ediyorum, ancak bunun daha geniş bir soyutlamanızı nasıl etkilediğini bilmiyorum. Yine de dikkate alınması gereken bir şey.


1
"Zeki" yorum için +1. Bu çok doğru bir şekilde bu örneği yakalar.

2
Next () 'nin “return and advance” eylemi, uygunsuz Java iterator arabirimi tarafından OP'ye zorlanır.
kevin cline

37

Tamamen tarz açısından, şu üç çizgiyi düşünüyorum:

T current = next;
next = fetcher.getNext();
return current;

... her ikisi de bir deneme / nihayet bloktan daha açık ve kısa. Herhangi bir istisna atılmamasını beklemiyorsanız, bir tryblok kullanmak sadece insanları şaşırtacak.


2
Bir dene / yakala / sonunda bir blok ek yükü de ekleyebilir, bir İstisna atmasanız bile, javac'ın veya JIT derleyicisinin bunları çıkaracağından emin değilim.
Martijn Verburg

2
@MartijnVerburg evet, javac hala istisna tablosuna bir giriş ekler ve try bloğu boş olsa bile nihayet kodunu çoğaltır.
Daniel Lubarov

@Daniel - teşekkürler javap'ı yürütemeyecek kadar tembeldim :-)
Martijn Verburg

@Martijn Verburg: AFAIK, bir try-catch-finallt bloğu, gerçek bir istisna olmadığı sürece esasen ücretsizdir. Burada, javac denemez ve JIT'in ilgileneceği bir şeyi optimize etmek gerekmez.
maaartinus

@ maaartinus - mantıklı geliyor - OpenJDK kaynak kodunu tekrar okumanız gerekiyor :-)
Martijn Verburg

6

Bu , nihayet bloğun içindeki kodun amacına bağlı olacaktır . Kurallı örnek, okuduktan / yazdıktan sonra bir akışı kapatıyor, her zaman yapılması gereken bir çeşit "temizleme". Bir nesneyi (bu durumda bir yineleyiciyi) geçerli bir duruma geri yüklemek IMHO da temizleme olarak sayılır, bu yüzden burada sorun görmüyorum. OTOH return, geri dönüş değeriniz bulunur bulunmaz kullandıysanız ve nihayet blokta bir sürü ilgisiz kod eklerseniz, bu amaç belirsizleşir ve daha az anlaşılır hale gelirdi.

“İçerisinde istisna yoktur” olduğunda kullanımda herhangi bir sorun görmüyorum. Bir kod try...finallyolmadan catchyalnızca kodun atabildiği RuntimeExceptionve bunları kullanmayı düşünmüyorsanız kullanmak çok yaygındır . Bazen, nihayet sadece bir güvenlik önlemidir ve programınızın mantığına hiçbir istisna atılmayacağını bilirsiniz (klasik “bu asla böyle olmaz” koşulu).

Tuzaklar: trybloğun içinde ortaya çıkan herhangi bir istisna finallybock çalışmasını sağlayacaktır . Bu sizi tutarsız bir duruma sokabilir. Öyleyse, iade ifadeniz şöyle bir şeyse:

return preProcess(next);

ve bu kod bir istisna yarattı, fetchNexthala çalışıyordu. OTOH gibi kodladıysanız:

T ret = preProcess(next);
next = fetcher.fetchNext(next);
return ret;

o zaman kaçmazdı. Deneme kodunun asla bir istisna yaratmayacağını varsaydığınızı biliyorum , ancak bundan daha karmaşık durumlar için nasıl emin olabilirsiniz? Yineleyiciniz uzun ömürlü bir nesne olsaydı, geçerli iş parçacığında kurtarılamaz bir hata olsa bile, varolan her zaman geçerli bir durumda tutulması önemlidir. Aksi takdirde, gerçekten önemli değil ...

Performans: Kaputun altında nasıl çalıştığını görmek için bu kodun ayrıştırılması ilginç olurdu, ama JVM'nin iyi bir tahminde bulunacak kadar bile bilmiyorum ... İstisnalar genellikle "istisnai", yani işleyen kod Bunların hız için optimize edilmelerine gerek yoktur (bu nedenle programlarınızın normal kontrol akışında istisnaları asla kullanmamanız tavsiye edilir), ama bilmiyorum finally.


Öyleyse, bu şekilde kullanmaktan kaçınmak için gerçekten iyi bir neden sunmuyorfinally musunuz? Demek istediğim, dediğiniz gibi, kodun istenmeyen sonuçları doğuruyor; Daha da kötüsü, muhtemelen istisnalar hakkında düşünmüyorsunuz bile.
Andres F.

2
Normal kontrol akış hızı istisna işleme yapılarından önemli ölçüde etkilenmez. Performans cezası, normal çalışma sırasında bir deneme bloğuna girerken veya bir son bloğuna girerken değil, yalnızca istisnalar atarken ve yakalanırken ödenir.
yfeldblum

1
@AndresF. Doğru, ancak bu herhangi bir temizleme kodu için de geçerlidir . Örneğin, açık bir akıma yapılan referansın nullbir buggy kodu ile ayarlandığını varsayalım ; ondan okumaya çalışmak bir istisna finallyyaratacaktır , blokta kapatmaya çalışmak başka bir istisna yaratacaktır . Ayrıca, bu hesaplama durumunun önemli olmadığı bir durumdur, bir istisna olup olmadığına bakılmaksızın akışı kapatmak istiyorsunuz. Bunun gibi başka durumlar da olabilir. Bu nedenle, onu asla kullanmamayı önermek yerine geçerli kullanımlar için bir tuzak olarak görüyorum.
mgibsonbr

Hem deneme hem de nihayet blok bir istisna attığında, denemedeki istisnanın hiç kimse tarafından görülmediği, ancak sorunun nedeninin bu olduğu da bir problem.
Hans-Peter Störr

3

Başlık ifadesi: "... nihayet döndükten sonra iş yapmak için yan tümce ..." yanlıştır. Son blok, fonksiyon dönmeden önce olur. Nihayetinde asıl mesele bu.

Kötüye kullandığınız şey, değerlendirme sırasıdır, sonraki değerin, değiştirilmeden önce geri dönmek için depolandığı yerdir. Bu yaygın bir uygulama değildir ve bence sizi sıralı olmayan kodlar ve dolayısıyla takip etmeleri daha zor hale getirdiği için yanlıştır.

Nihai blokların kullanım amacı, atılan istisnalar hariç, örneğin bir kaynağı temizlemek, DB bağlantısını kapatmak, bir dosyayı / soketi kapatmak vb.


+1, eğer dil belirtimi değerin geri verilmeden önce saklandığını garanti etse bile, büyük olasılıkla okuyucunun beklentisine aykırı olduğunu ve makul olarak mümkün olduğunda kaçınılması gerektiğini ekleyeceğim. Hata ayıklama kodlamanın iki katı ...
jmoreno

0

Gerçekten dikkatli olurdum, çünkü (genel olarak, sizin örneğinizde değil) denemedeki bir kısmı istisna atabilir ve atılırsa hiçbir şey iade edilmeyecektir ve gerçekte nihayetinde olanı çalıştırmak için giriştiyseniz, istemediğin zaman yürü.

Diğer solucan kabı, genel olarak, nihayetinde bazı yararlı eylemlerin istisnalar atabildiğini, bu yüzden gerçekten dağınık bir yöntemle son bulabileceğinizi belirtiyor.

Bu nedenle, onu okuyan birinin bu sorunları düşünmesi gerekir, bu yüzden anlaşılması daha zordur.

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.