Temel olarak, yansıma, programınızın kodunu veri olarak kullanmak anlamına gelir.
Bu nedenle, programınızın kodu faydalı bir veri kaynağı olduğunda yansıma kullanmak iyi bir fikir olabilir. (Ancak takaslar vardır, bu yüzden her zaman iyi bir fikir olmayabilir.)
Örneğin, basit bir sınıf düşünün:
public class Foo {
public int value;
public string anotherValue;
}
ve ondan XML oluşturmak istiyorsunuz. XML'i oluşturmak için kod yazabilirsiniz:
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
Fakat bu çok fazla kazan kodudur ve sınıfı her değiştirdiğinizde kodu güncellemeniz gerekir. Gerçekten, bu kodun ne yaptığını tanımlayabilirsiniz.
- sınıfın ismiyle bir XML elemanı yaratın
- sınıfın her özelliği için
- özelliğin adıyla bir XML öğesi oluşturun
- özelliğin değerini XML öğesinin içine koy
- XML öğesini köke ekle
Bu bir algoritmadır ve algoritmanın girdisi sınıftır: onun adına ve özelliklerinin adlarına, türlerine ve değerlerine ihtiyacımız var. Yansımanın geldiği yer burasıdır: size bu bilgilere erişim sağlar. Java, Class
sınıfın yöntemlerini kullanarak türleri denetlemenizi sağlar .
Bazı daha fazla kullanım durumları:
- Bir web sunucusundaki URL'leri bir sınıfın yöntem adlarına ve yöntem argümanlarına göre URL parametrelerine göre tanımlar.
- bir sınıfın yapısını GraphQL tip tanımına dönüştürmek
- ünite test durumu olarak adı "test" ile başlayan bir sınıfın her yöntemini çağırın
Bununla birlikte, tam yansıtma sadece varolan koda bakmak (kendi başına "iç gözlem" olarak da bilinir) değil, aynı zamanda kodu değiştirmek veya üretmek anlamına gelir. Bunun için Java’da iki önemli kullanım durumu vardır: proxy'ler ve alaylar.
Arayüzünüz olduğunu varsayalım:
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
ve ilginç bir şey yapan bir uygulamanız var:
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
Ve aslında sizin de ikinci bir uygulamanız var:
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
Şimdi ayrıca bazı günlük çıktıları istiyorsunuz; ne zaman bir yöntem çağrılırsa sadece bir günlük mesajı istersiniz. Açıkça her yönteme günlük çıktısı ekleyebilirsiniz, ancak bu can sıkıcı olurdu ve bunu iki kez yapmanız gerekirdi; Her uygulama için bir kez. (Daha fazla uygulama eklediğinizde daha da fazla.)
Bunun yerine, bir proxy yazabilirsiniz:
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
Yine de, bir algoritma ile tanımlanabilen tekrarlayan bir desen vardır:
- logger proxy, bir arayüz uygulayan bir sınıftır
- Arabirimin başka bir uygulamasını alan bir yapıcı ve bir kaydedici var
- Arayüzdeki her yöntem için
- uygulama "$ methodname adlı" mesajını kaydeder
- ve sonra, tüm argümanlardan geçen iç arayüzde aynı yöntemi çağırır.
ve bu algoritmanın girişi arayüz tanımıdır.
Yansıma, bu algoritmayı kullanarak yeni bir sınıf tanımlamanıza izin verir. Java bunu java.lang.reflect.Proxy
sınıfın yöntemlerini kullanarak yapmanızı sağlar ve size daha da fazla güç veren kütüphaneler vardır.
Peki yansımanın olumsuz tarafları nelerdir?
- Kodunuzun anlaşılması zorlaşıyor. Kodunuzun somut etkilerinden bir kez daha soyutlanmışsınız.
- Kodunuz hata ayıklamak zorlaşıyor. Özellikle kod üreten kitaplıklarda, yürütülen kod yazdığınız kod olmayabilir, ancak oluşturduğunuz kod olabilir ve hata ayıklayıcı bu kodu size gösteremeyebilir (veya kesme noktaları koymanıza izin verebilir).
- Kodunuz yavaşlar. Dinamik olarak tip bilgisini okumak ve alanlara kodlama erişimi yerine çalışma zamanı tutamaçlarına göre erişmek daha yavaştır. Dinamik kod oluşturma, hata ayıklamanın daha da zor olması pahasına bu etkiyi azaltabilir.
- Kodunuz daha kırılgan hale gelebilir. Dinamik yansıma erişimi derleyici tarafından yazılmaz, ancak çalışma zamanında hatalar atar.