Yansımayı (aşırı) kullanmak kötü bir alışkanlık mıdır?


17

Isı levhası kodunun miktarını büyük ölçüde azaltırsa yansıma kullanmak iyi bir uygulama mı?

Temel olarak bir tarafta performans ve belki de okunabilirlik ile diğer tarafta kazan plakası kodunun soyutlanması / otomasyonu / azaltılması arasında bir denge vardır.

Düzenleme: İşte önerilen bir yansıma kullanımı örneği .

Bir örnek vermek gerekirse, bir soyut bir sınıf olduğunu varsayalım Base10 alan ve 3 alt sınıfları vardır SubclassA, SubclassBve SubclassC10 farklı alanların her biri; hepsi basit fasulye. Sorun iki Basetür başvuru almak ve karşılık gelen nesnelerin aynı (alt) türü ve eşit olup olmadığını görmek istediğiniz olmasıdır.

Çözümler olarak, türlerin eşit olup olmadığını ilk önce kontrol ettiğiniz ve sonra tüm alanları kontrol ettiğiniz veya yansımayı kullanıp aynı türden olup olmadıklarını dinamik olarak görebileceğiniz ve "get" (kongre) ile başlayan tüm yöntemler üzerinde yineleme yapabileceğiniz ham bir çözüm vardır. her iki nesnede de çağırın ve sonuçlarda çağrı eşittir.

boolean compare(Base base1, Base, base2) {
    if (base1 instanceof SubclassA && base2 instanceof SubclassA) { 
         SubclassA subclassA1 = (SubclassA) base1;
         SubclassA subclassA2 = (SubclassA) base2;
         compare(subclassA1, subclassA2);
    } else if (base1 instanceof SubclassB && base2 instanceof SubclassB) {
         //the same
    }
    //boilerplate
}

boolean compare(SubclassA subA1, SubclassA subA2) {
    if (!subA1.getField1().equals(subA2.getField1)) {
         return false;
    }
    if (!subA1.getField2().equals(subA2.getField2)) {
         return false;
    }
    //boilerplate
}

boolean compare(SubclassB subB1, SubclassB subB2) {
    //boilerplate
}

//boilerplate

//alternative with reflection 
boolean compare(Base base1, Base base2) {
        if (!base1.getClass().isAssignableFrom(base2.getClass())) {
            System.out.println("not same");
            System.exit(1);
        }
        Method[] methods = base1.getClass().getMethods();
        boolean isOk = true;
        for (Method method : methods) {
            final String methodName = method.getName();
            if (methodName.startsWith("get")) {
                Object object1 = method.invoke(base1);
                Object object2 = method.invoke(base2);
                if(object1 == null || object2 == null)  {
                    continue;
                }
                if (!object1.equals(object2)) {
                    System.out.println("not equals because " + object1 + " not equal with " + object2);
                    isOk = false;
                }
            }
        }

        if (isOk) {
            System.out.println("is OK");
        }
}

20
Herhangi bir şeyin aşırı kullanımı kötü bir alışkanlıktır.
Tulains Córdova

1
@ user61852 Doğru, çok fazla özgürlük diktatörlüğe yol açar. Bazı yaşlı Yunanlılar bunu zaten biliyordu.
ott--

6
“Çok fazla su senin için kötü olur. Açıkçası, çok fazla olan şey aşırı olan miktardır - bunun anlamı budur! ”- Stephen Fry
Jon Purdy

4
"Nesne T1 türündeyse
rm5248

Yanıtlar:


25

Yansıma ne benzer, derleme zamanında bilinmeyen bir sınıfın işlevleri keşfetmek için, belirli bir amaç için oluşturulmuş dlopenve dlsymgereken işlevleri C. o herhangi bir kullanımı dışında ağır incelenip.

Java tasarımcılarının kendilerinin bu sorunla karşılaştıkları hiç oldu mu? Bu yüzden pratikte her sınıfın bir equalsyöntemi var. Farklı sınıfların farklı eşitlik tanımları vardır. Bazı durumlarda, türetilmiş bir nesne bir temel nesneye eşit olabilir. Bazı durumlarda eşitlik, alıcıları olmayan özel alanlara göre belirlenebilir. Bilmiyorsun.

Bu nedenle, özel eşitlik isteyen her nesne bir equalsyöntem uygulamalıdır . Sonunda, nesneleri bir kümeye koymak veya bir karma indeksi olarak kullanmak isteyeceksiniz, o zaman equalsyine de uygulamanız gerekecektir. Diğer diller farklı yapar, ancak Java kullanır equals. Dilinizin kurallarına bağlı kalmalısınız.

Ayrıca, "kazan plakası" kodu, doğru sınıfa konursa, vidalanması oldukça zordur. Yansıma ek karmaşıklık ekler, yani hatalar için ek şanslar. Örneğin, yönteminizde, biri nullbelirli bir alan için geri döner ve diğeri dönmezse iki nesne eşit kabul edilir . Alıcılarınızdan biri uygun olmadan nesnelerinizden birini döndürürse ne olur equals? Sizin if (!object1.equals(object2))başarısız olur. Ayrıca hataya eğilimli hale getirmek, yansımanın nadiren kullanıldığı gerçeğidir, bu nedenle programcılar gotcha'larına aşina değildir.


12

Yansımanın aşırı kullanımı muhtemelen kullanılan dile bağlıdır. Burada Java kullanıyorsunuz. Bu durumda, yansıma dikkatle kullanılmalıdır, çünkü genellikle sadece kötü tasarım için bir çözümdür.

Yani, farklı sınıfları karşılaştırıyorsunuz, bu yöntem geçersiz kılma için mükemmel bir sorundur . İki farklı sınıf örneğinin asla eşit kabul edilmemesi gerektiğini unutmayın. Eşitliği ancak aynı sınıftan örnekleriniz varsa karşılaştırabilirsiniz. Eşitlik karşılaştırmasının doğru bir şekilde nasıl uygulanacağına ilişkin bir örnek için /programming/27581/overriding-equals-and-hashcode-in-java adresine bakın .


16
+1 - Bazılarımız, HERHANGİ bir yansıma kullanımının kötü tasarımı gösteren kırmızı bir bayrak olduğuna inanıyoruz.
Ross Patterson

1
Büyük olasılıkla bu durumda eşitliğe dayanan bir çözüm arzu edilir, ancak genel bir fikir olarak yansıma çözümünün nesi yanlış? Aslında çok geneldir ve eşittir yöntemlerin her sınıf ve alt sınıfta açıkça yazılmasına gerek yoktur (ancak iyi bir IDE tarafından kolayca oluşturulabilirler).
m3th0dman

2
@RossPatterson Neden?
m3th0dman

2
@ m3th0dman Çünkü compare()"get" ile başlayan herhangi bir yöntemin alıcı olduğunu ve karşılaştırma işleminin bir parçası olarak çağrılması güvenli ve uygun olduğunu varsayarak yönteminiz gibi şeylere yol açar . Bu, nesnenin amaçlanan arayüz tanımının ihlali anlamına gelir ve bu uygun olsa da, neredeyse her zaman yanlıştır.
Ross Patterson

4
@ m3th0dman Kapsüllemeyi ihlal eder - temel sınıf alt sınıflarındaki özelliklere erişmek zorundadır. Özel (veya alıcı özel) ise ne olur? Polimorfizm ne olacak? Başka bir alt sınıf eklemeye karar verirsem ve karşılaştırmayı farklı bir şekilde yapmak istiyorsam? Temel sınıf bunu zaten benim için yapıyor ve değiştiremiyorum. Ya alıcılar bir şey tembel yüklerse? Karşılaştırma yönteminin bunu yapmasını ister miyim? Hatta getbir yöntemi döndüren özel bir yöntem değil bir alıcı olduğunu bir yöntem olduğunu nasıl bilebilirim ?
Sulthan

1

Bence burada iki sorunun var.

  1. Ne kadar dinamik ve statik koda sahip olmalıyım?
  2. Eşitliğin özel bir sürümünü nasıl ifade edebilirim?

Dinamik ve Statik Kod

Bu çok yıllık bir soru ve cevap çok tartışılıyor.

Bir yandan derleyiciniz her türlü kötü kodu yakalamada çok iyidir. Bunu çeşitli analiz biçimleriyle yapar, Tip analizi ortak bir yöntemdir. BananaKodda a Cog. Bekleyen bir nesneyi kullanamayacağınızı bilir . Bunu bir derleme hatasıyla söyler.

Şimdi bunu ancak hem kabul edilen türü hem de verilen türü bağlamdan çıkarabildiğinde yapabilir. Ne kadar çıkarılabilir ve bu çıkarımın ne kadar yaygın olduğu büyük ölçüde kullanılan dile bağlıdır. Java, Kalıtım, Arayüzler ve Jenerikler gibi mekanizmalar aracılığıyla tür bilgilerini çıkarabilir. Kilometre değişir, diğer bazı diller daha az mekanizma sağlar ve bazıları daha fazlasını sağlar. Hala derleyicinin gerçek olduğunu bildikleri şeylerle kaynaşır.

Öte yandan derleyiciniz yabancı kodun şeklini tahmin edemez ve bazen dilin tür sistemi kullanılarak kolayca ifade edilemeyen birçok tür üzerinde genel bir algoritma ifade edilebilir. Bu durumlarda derleyici sonucu her zaman önceden bilemez ve hangi soruyu soracağını bile bilemeyebilir. Yansıma, arabirimler ve Object sınıfı Java'nın bu sorunları ele alma yoludur. Doğru kontrolleri ve işlemleri sağlamanız gerekecek, ancak bu tür kodlara sahip olmak sağlıksız değil.

Kodunuzu çok özel veya çok genel yapmak, ele almaya çalıştığınız soruna gelir. Eğer bunu tip sistemini kullanarak kolayca ifade edebilseydiniz. Derleyicinin güçlü yönlerini oynamasına ve size yardımcı olmasına izin verin. Eğer tür sistemi önceden bilmiyorsa (yabancı kod) veya tür sistemi algoritmanızın genel bir uygulaması için uygun değilse, yansıma (ve diğer dinamik araçlar) kullanmak için doğru araçtır.

Sadece dilinizin tip sisteminin dışına çıkmanın şaşırtıcı olduğunu unutmayın. Arkadaşınıza doğru yürüdüğünüzü ve İngilizce konuştuğunuzu düşünün. Aniden düşüncelerinizi tam olarak ifade eden İspanyolca, Fransızca ve Kantonca'dan birkaç kelime bırakın. Bağlam arkadaşınıza çok şey söyleyecektir, ancak aynı zamanda her türlü yanlış anlamalara yol açan bu kelimeleri nasıl kullanacaklarını tam olarak bilmeyebilirler. Bu yanlış anlamalarla ilgilenmek , İngilizce'deki fikirleri daha fazla kelime kullanarak açıklamaktan dahaiyi ?

Özel Eşitlik

Her ne kadar Java'nın equalsgenellikle iki nesneyi karşılaştırma yöntemine büyük ölçüde bağlı olduğunu anlasam da, belirli bir bağlamda her zaman uygun değildir.

Başka bir yol var ve bu da bir Java standardı. Buna Karşılaştırıcı denir .

Karşılaştırıcınızı nasıl uyguladığınız, karşılaştırdığınız şeye ve nasıl olduğuna bağlı olacaktır.

  • Özel equalsuygulamalarına bakılmaksızın iki nesneye uygulanabilir.
  • Herhangi iki nesneyi ele almak için genel (yansıma tabanlı) bir karşılaştırma yöntemi uygulayabilir.
  • Ortak plaka karşılaştırma fonksiyonları, yaygın olarak karşılaştırılan nesne türleri için eklenebilir, tip güvenliği ve optimizasyonu sağlanır.

1

Yansıtıcı programlamadan mümkün olduğunca kaçınmayı tercih ederim çünkü

  • kodun derleyici tarafından statik olarak kontrol edilmesini zorlaştırır
  • kodun akıl yürütmesini zorlaştırır
  • kodu yeniden düzenleme zorlaştırır

Ayrıca basit yöntem çağrılarından çok daha az performans gösterir; bir büyüklük veya daha büyük bir sıra ile yavaşlardı.

Statik Kontrol Kodu

Herhangi bir yansıtıcı kod, dizeleri kullanarak sınıfları ve yöntemleri arar; orijinal örnekte "get" ile başlayan herhangi bir yöntem arar; alıcıları ancak "gettysburgAddress ()" gibi diğer yöntemleri de döndürür. Kurallar kodda sıkılaştırılabilir, ancak mesele bunun bir çalışma zamanı denetimi olduğu ; bir IDE ve derleyici yardımcı olamaz. Genel olarak "stringly typed" veya "ilkel obsessed" kodunu beğenmedim.

Harder to Reason Hakkında

Yansıtıcı kod, basit yöntem çağrılarından daha ayrıntılıdır. Daha fazla kod = daha fazla hata veya hata için en az daha fazla potansiyel, daha fazla kod okunması, test edilmesi vb.

Refactor İçin Daha Zor

Kod, dize / dinamik tabanlı olduğundan; yansıma sahnede olur olmaz IDE'nin yansıtıcı kullanımlarını alamadığı için IDE'nin yeniden düzenleme araçlarını kullanarak kodu% 100 güvenle yeniden düzenleyemezsiniz.

Temel olarak, mümkünse genel kodda düşünmekten kaçının; gelişmiş bir tasarım arayın.


0

Tanım gereği aşırı kullanım kötü, değil mi? Şimdilik (üstünden) kurtulalım.

Baharın inanılmaz derecede yoğun iç yansımasını kötü bir alışkanlık olarak adlandırır mısın?

Bahar ek açıklamaları kullanarak yansımayı iyileştirir - Hazırda Beklet (ve muhtemelen düzinelerce / yüzlerce başka araç).

Kendi kodunuzda kullanıyorsanız bu modelleri izleyin. Kullanıcınızın IDE'sinin yine de onlara yardımcı olabileceğinden emin olmak için ek açıklamaları kullanın (Kodunuzun tek "Kullanıcısı siz olsanız bile, dikkatsiz yansıma kullanımı muhtemelen sizi popoda ısırmaya geri dönecektir).

Bununla birlikte, kodunuzun geliştiriciler tarafından nasıl kullanılacağı dikkate alınmadan, en basit yansıma kullanımı bile aşırı kullanımdır.


0

Bence bu cevapların çoğu bu noktayı kaçırıyor.

  1. Evet, muhtemelen yazmalısınız equals()ve hashcode()@KarlBielefeldt tarafından belirtildiği gibi.

  2. Ancak, birçok alanı olan bir sınıf için, bu sıkıcı bir kazan plakası olabilir.

  3. Yani, duruma göre değişir .

    • Sadece eşittir ve hashcode'a nadiren ihtiyacınız varsa , genel amaçlı bir yansıma hesaplaması kullanmak pragmatik ve muhtemelen tamamdır. En azından hızlı ve kirli bir ilk geçiş olarak.
    • ancak çok fazla ihtiyacınız varsa, örneğin bu nesneler HashTable'lara konur, böylece performans bir sorun olur, kesinlikle kodu yazmalısınız. çok daha hızlı olacak.
  4. Başka bir olasılık: eğer sınıflarınız gerçekten eşit bir alanı yazmak o kadar çok alana sahipse, düşünün

    • alanları Haritaya yerleştirme.
    • yine de özel alıcılar ve ayarlayıcılar yazabilirsiniz
    • Map.equals()karşılaştırma için kullanın . (veya Map.hashcode())

ör. ( not : null kontrolleri yoksaymak, büyük olasılıkla gösterilmeyen String anahtarları yerine Enums kullanmalıdır ...)

class TooManyFields {
  private HashMap<String, Object> map = new HashMap<String, Object>();

  public setFoo(int i) { map.put("Foo", Integer.valueOf(i)); }
  public int getFoo()  { return map.get("Foo").intValue(); }

  public setBar(Sttring s) { map.put("Bar", s); }
  public String getBar()  { return map.get("Bar").toString(); }

  ... more getters and setters ...

  public boolean equals(Object o) {
    return (o instanceof TooManyFields) &&
           this.map.equals( ((TooManyFields)o).map);
}
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.