Reflection (תכנות)

במדעי המחשב, reflection (ריפלקשן; מילולית מאנגלית: השתקפות או התבוננות) היא היכולת של תוכנת מחשב לבחון אובייקטים או לשנות את התנהגותם בזמן ריצה. בחינת האובייקטים מבוצעת באמצעות תהליך הנקרא type introspection - "הסתכלות פנימה על טיפוסים", ושינוי ההתנהגות של אובייקטים כוללת את היכולת לשנות ערכים, metadata, תכונות (properties) ופונקציות.
השימוש הנפוץ ביותר ב-reflection הוא בשפות עילית הרצות על גבי מכונה וירטואלית, כדוגמת Smalltalk. כמו כן השימוש ב-reflection נפוץ בשפות תסריט ובשפות בעלות טיפוסיות סטטית כגון: ML, Java, ו-Haskell.
לשפות תכנות התומכות ב-reflection קוראים "שפות רפלקטיביות" (reflective languages). לתכנות הכולל שימוש ב-reflection קוראים "תכנות מונחה-reflection-oriented programming) "reflection).

רקע היסטורי

התכנות של מחשבים מוקדמים בוצע בשפת הסף הטבעית שלהם. שפות סף כאלה הן רפלקטיביות מטבען, מכיוון שהן פועלות על ידי הגדרת ההוראות בצורה של נתונים. פונקציונליות רפלקטיבית כגון שינוי ההוראות או ניתוח שלהן הייתה דבר שבשגרה. כאשר התכנות התקדם לשפות תכנות עיליות יותר כדוגמת שפת C, השימוש ב-reflection נעלם, עד שהופיעו שפות תכנות שמערכת הטיפוסים שלהן כוללת תמיכה ב-reflection.

שימושים

ניתן להשתמש ב-reflection כדי לבחון את ביצוע התוכנית ולהשפיע עליה בזמן ריצה. רכיב בתוכנה מונחית-reflection יכול לפקח אחר הביצוע של יחידת קוד, ולשנות את עצמו בהתאם למטרה שרוצים להשיג בהקשר ליחידת קוד זו. בדרך כלל זה נעשה על ידי הקצאת קוד בזמן ריצה.
בשפות תכנות מונחות-עצמים כמו Java, שימוש ב-reflection מאפשר בדיקה בזמן ריצה של מחלקות, ממשקים, שדות ומתודות, מבלי לדעת את השמות שלהם בזמן הידור. כמו כן זה מאפשר יצירת מופעים חדשים של אובייקטים וקריאה למתודות.
בנוסף לכך, ניתן להשתמש ב-reflection על מנת לסגל תוכניות למצבים שונים בצורה דינאמית. לדוגמה, נחשוב על יישום המשתמש לסירוגין בשתי מחלקות שונות, X ו-Y, לצורך ביצוע של משימות דומות. בלי תכנות מונחה-reflection, יכול להיות שהיישום יהיה מתוכנת בצורה קשיחה (hard-coded) לקרוא לשמות של מתודות של מחלקה X ומחלקה Y. לעומת זאת, על ידי שימוש בפרדיגמת התכנות מונחה-reflection, ניתן לממש את היישום כך שישתמש ב-reflection כדי לקרוא למתודות במחלקות X ו-Y, מבלי לקודד מראש בצורה קשיחה את שמות המתודות.
על מנת לנצל את היתרונות שבכתיבת קוד גנרי יותר באמצעות תכנות מונחה-reflection, כמעט תמיד נדרשות מומחיות ותשתיות תוכנה נוספות. מידת השימוש ב-reflection קובעת עד כמה ניתן יהיה להימנע משימוש ב-hard coding (קידוד קשיח).
לעיתים קרובות משתמשים ב-reflection בבדיקות תוכנה, כאשר רוצים ליצור מופעים של "אובייקטי דמה" (mock objects) בזמן ריצה.
כמו כן, reflection היא האסטרטגיה הראשית ב-metaprogramming – כתיבת תוכנות מחשב שכותבות או משנות תוכנות אחרות.

דוגמאות

קטעי הקוד הבאים יוצרים מופע foo של המחלקה Foo, ואז קוראים למתודה hello. עבור כל שפת תכנות, מוצגות הקריאות הרגילות וגם הקריאות שעושות שימוש ב-reflection.

Java

// Using Java package: java.lang.reflect// Without reflectionnew Foo().hello();// With reflectionClass<?> clazz = Class.forName("Foo");clazz.getMethod("hello").invoke(clazz.newInstance());

JavaScript

// Without reflectionnew Foo().hello()// With reflection// assuming that Foo resides in thisnew this['Foo']()['hello']()// or without assumptionnew (eval('Foo'))()['hello']()// or simplyeval('new Foo().hello()')

PHP

// without reflection$foo = new Foo();$foo->hello();// with reflection$reflector = new ReflectionClass('Foo');$foo = $reflector->newInstance();$hello = $reflector->getMethod('hello');$hello->invoke($foo);// using callback$foo = new Foo();call_user_func(array($foo, 'hello'));// using variable variables syntax$className = 'Foo';$foo = new $className();$method = 'hello';$foo->$method();

Ruby

# without reflectionobj = Foo.newobj.hello# with reflectionclass_name = "Foo"method = :helloobj = Kernel.const_get(class_name).newobj.send method

Objective-C

// Foo class@interface Foo : NSObject- (void)hello;//...@end// without reflectionFoo *obj = [[Foo alloc] init];[obj hello];// with reflection in OPENSTEP, loading class and call method using variables.NSString *className = @"Foo";SEL selector = @selector(hello);id obj = [[NSClassFromString(className) alloc] init];[obj performSelector:selector]; // This will emit an warning when compiling with Objective-C ARC.