Yansıması olan genel bir belirleyici ve alıcı oluşturmak neden kötü bir fikir?


49

Bir süre önce bu cevabı , her değişken değişken için alıcı ve ayarlayıcıdan nasıl kaçınacağına dair bir soruya yazdım . O zamanlar, bunun Kötü bir Fikir olduğunu hissetmesi için sadece sözlü olarak zorlaştırılan bir bağırsak vardı, ama OP açıkça nasıl yapıldığını soruyordu. Burada bunun neden bir sorun olabileceğini araştırdım ve bu soruyu buldum , cevapları kesinlikle gerekli olmadıkça yansıma kullanmanın kötü bir uygulama olmadığı gerçeğini kabul ediyor.

Öyleyse, birisi, özellikle genel alıcı ve belirleyici durumunda, yansımanın önlenmesi gerektiğinin neden verilmiş olduğu konusunda sözlü olabilir mi?


12
Sadece kazan plakasını azaltmak istiyorsanız, hem Lombok hem de Groovy sessiz ve güvenli bir şekilde alıcı ve ayarlayıcılar üretecektir.
chrylis

2
Bu alıcıları ve ayarlayıcıları java gibi statik bir dilde nasıl tüketirsiniz? Bana bir başlangıç ​​olmayan gibi görünüyor.
Esben Skov Pedersen

@EsbenSkovPedersen Hiç de gibi onları tüketmek istiyorum Map's getve putşeyleri yapmanın oldukça aksak bir yoldur.
HAEM

2
IIRC, Lombok uygun açıklamalarda belirtildiği gibi derleme zamanında alıcıları / ayarlayıcıları sentezler, bu nedenle bunlar: yansıma performans cezasına maruz kalmazlar, statik olarak analiz edilir / satırlar çizilirler, yeniden yansıtılabilirler (IntelliJ, Lombok için bir eklentiye sahiptir)
Alexander,

Yanıtlar:


117

Genel olarak yansımanın olumsuz tarafları

Yansıtmanın anlaşılması, düz çizgi koddan daha zordur.

Tecrübelerime göre, yansıma Java'da "uzman düzeyinde" bir özelliktir. Çoğu programcının yansımayı hiçbir zaman aktif olarak kullanmadığını (yani yansıma kullanan kütüphaneleri tüketmenin sayılmadığını) savunuyorum. Bu kod, bu programcılar için anlaşılmasını zorlaştırıyor.

Yansıtma kodu statik analize erişilemiyor

Diyelim ki getFoosınıfımda bir alıcı var ve adını değiştirmek istiyorum getBar. Eğer yansıma kullanmazsam, sadece kod tabanını araştırabilirim ve getFooalıcıyı kullanan her yeri bulabilirim, böylece güncelleyebilirim ve birini kaçırsam bile derleyici şikayet eder.

Ancak alıcıyı kullanan yer bir şeye benziyorsa callGetter("Foo")ve callGetteryaparsa getClass().getMethod("get"+name).invoke(this), yukarıdaki yöntem onu ​​bulamaz ve derleyici şikayet etmez. Yalnızca kod gerçekten çalıştırıldığında bir a alırsınız NoSuchMethodException. Ve bu istisnanın (izlenen) acı çektiğini hayal edin, callGetterçünkü “yalnızca kodlanmış dizelerle kullanılır, aslında olamaz”. (Kimse bunu yapmaz, birisi tartışabilir mi? OP'nin SO cevabında aynen bunu yapması dışında . Alanın adı değiştirilirse, genel belirleyicinin kullanıcıları, belirleyicinin son derece belirsiz hatası dışında sessizce hiçbir şey yapmamanın farkına varamazlardı. Alıcı kullanıcılar, eğer şanslılarsa, göz ardı edilen istisnanın konsol çıktısını görebilirler.)

Yansıtma kodu derleyici tarafından kontrol edilmedi

Bu, temelde yukarıdakilerin büyük bir alt noktasıdır. Yansıma kodu tamamen ilgili Object. Çalışma zamanında tipler kontrol edilir. Hatalar, birim testleri tarafından keşfedilir, ancak yalnızca kapsamınız varsa. (“Sadece bir alıcı, test etmem gerekmiyor.”) Temelde, Python üzerinden Java kullanarak avantajınızı kaybedersiniz.

Yansıtma kodu optimizasyon için kullanılamıyor

Belki teoride değil, pratikte, için bir satır içi önbellek sıralayan veya oluşturan bir JVM bulamazsınız Method.invoke. Bu tür optimizasyonlar için normal yöntem çağrıları yapılabilir. Bu onları çok daha hızlı yapar.

Yansıtma kodu genel olarak sadece yavaş

Yansıma kodu için gerekli olan dinamik yöntem arama ve tür denetimi normal yöntem çağrılarından daha yavaştır. Bu ucuz tek hatlı alıcıyı bir yansıma canavarına çevirirseniz, (bunu ölçmedim) birkaç yavaşlama derecesine bakıyor olabilirsiniz.

Genel alıcı / ayarlayıcı için olumsuz

Bu sadece kötü bir fikir, çünkü sınıfınızın artık enkapsülasyonu yok. Sahip olduğu her alana erişilebilir. Hepsini herkese açık hale getirebilirsin.


8
Tüm önemli noktaları vurdun. Hemen hemen mükemmel bir cevap. Alıcıların ve pasacının, halka açık alanlardan marjinal olarak daha iyi olduklarını, ancak daha büyük noktaların doğru olduğunu söyleyebilirim.
JimmyJames,

2
Alıcıların / ayarlayıcıların bir IDE tarafından da kolayca üretilebileceğinden bahsetmeye değer.
stiemannkj1

26
+1 Bir üretim büyüklüğündeki projede hata ayıklama yansıma mantığı BM tarafından işkence olarak tanınmalı ve yasa dışı olmalıdır.
errantlinguist

4
"Bu programcılar için anlaşılması zor." - bu programcılar için anlaşılması oldukça güç, ancak ne kadar yetkin olursa olsun herkes için anlamak zor .
BartoszKP

4
"Yansıma kodu statik analize erişilemiyor" - Bu. Yeniden yapılanmayı etkinleştirmek için çok önemlidir. Statik analiz araçları daha da güçlendikçe (örneğin, en son Team Foundation Server'da kod arama), bunları sağlayan kod yazma daha da değerli hale gelir.
Dan J,

11

Çünkü geçiş alıcı / ayarlayıcıları, hiçbir değer sağlamayan bir istismardır. Kullanıcılar özellikler üzerinden gerçek değerlere ulaşabildiklerinden herhangi bir kapsülleme sağlamazlar. Bunlar YAGNI'dir, çünkü nadiren saha depolama alanınızın uygulamasını değiştirecek olursunuz.

Ve çünkü bu daha az performans gösteriyor. En azından C # 'da, bir alıcı / ayarlayıcı doğrudan tek bir CIL komutuna satır içi olacaktır. Cevabınız, bunu yansıtmak için meta verileri yüklemeniz gereken 3 işlev çağrısıyla değiştiriyorsunuz. Genelde 10x'i yansıtma maliyetleri için kural olarak kullandım, ancak verileri aradığımda ilk cevaplardan biri, 1000x'e yaklaşan bu cevabı içeriyordu .

(Ve kodunuzun hata ayıklamasını zorlaştırır ve kullanılabilirliği zedelemektedir, çünkü kötüye kullanmanın daha fazla yolu vardır ve uygunluğa zarar verir çünkü artık doğru tanımlayıcılardan ziyade sihirli dizeler kullanıyorsunuz ...)


2
Sorunun, Fasulye kavramı gibi hoş bir saçmalık olan Java olarak etiketlendiğini unutmayın. Bir çekirdeğin halka açık alanları yoktur, ancak alanları getX()ve setX()yansıma yoluyla bulunabilecek yöntemleri ortaya çıkarabilir. Fasulyenin, değerlerini kapsüllemesi gerekmiyor, sadece DTO'lar olabilir. Yani Java'da alıcılar genellikle gerekli bir acıdır. C # özellikleri her açıdan çok daha güzel.
amon

11
C # özellikleri giyiniyor alıcılar / ayarlayıcılar olsa. Ama evet, Fasulye kavramı bir felakettir.
Sebastian Redl

6
@ amon Doğru, C # çok kötü bir fikir aldı ve uygulanması daha kolay.
JimmyJames,

5
@JimmyJames Bu gerçekten korkunç bir fikir değil; kod değişikliklerine rağmen ABI uyumluluğunu koruyabilirsiniz. Sanırım çoğunun sahip olmadığı bir sorun var.
Casey,

3
@Casey Bunun kapsamı, yorumun ötesine geçiyor, ancak uygulamanın iç detaylarından bir arayüz oluşturmak geriye doğru. Usul bir tasarıma yol açar. Bir getX yöntemine sahip olmanın doğru cevap olduğu zamanlar vardır ancak iyi tasarlanmış kodlarda oldukça nadirdir. Java’da alıcıların ve belirleyicilerin sorunu, etraflarındaki ortak kod değil, korkunç bir tasarım yaklaşımının bir parçası. Bunu kolaylaştırmak, yüzünüze yumruk atmayı kolaylaştırmak gibidir.
JimmyJames,

8

Zaten sunulan iddialara ek olarak.

Beklenen arayüzü sağlamıyor.

Kullanıcılar foo.get ("bar") ve foo.set ("bar", değer) yerine foo.getBar () ve foo.setBar (değer) 'i çağırmayı bekler.

Kullanıcıların beklediği arayüzü sağlamak için, her özellik için ayrı bir alıcı / ayarlayıcı yazmanız gerekir. Bunu yaptıktan sonra yansıma kullanmanın bir anlamı yoktur.


Bana göre bu sebep, diğer cevaplarda diğerlerinden daha önemli.
Esben Skov Pedersen

-4

Tabii ki kötü bir fikir değil, java dünyasında olduğu gibi kötü bir fikir (sadece bir alıcı veya alıcı istediğinde). Örneğin, bazı çerçeveler (spring mvc) veya kütüphaneler zaten onu (jstl) bu şekilde kullanıyor, bazı json o xml ayrıştırıcıları kullanıyor, eğer bir DSL kullanmak istiyorsanız, onu kullanacaksınız. Öyleyse kullanım senaryosuna bağlıdır.

Hiçbir şey gerçek olarak doğru veya yanlış değildir.

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.