348. RealProxy

אפשר להמשיך את סדרת הטיפים של Proxy בשני אופנים:

הראשון הוא להתחיל לדון כיצד ניתן לממש Proxy

השני הוא להתחיל לפתוח ולחקור את העולם של Interception (זו הכללה לProxy)

אני אתחיל בסקירת שיטות למימוש Proxy.

כמו שראינו, הדרך הפשוטה ביותר לממש Proxy הוא לממש מחלקה המממשת ממשק ומנתבת את כלל המתודות למתודה מסוימת. זה קצת עבודה, אבל אפשרי לעשות זאת בלי יותר מדי רקע וידע.

הבעיה העיקרית בפתרון הזה, היא שצריך לממש עבור כל סוג ממשק מחלקה כזאת, כלומר לא נוכל לכתוב Proxy גנרי כלשהו. כמובן, זה בלתי מתקבל על הדעת ברוב המקרים, שהרי אם WCF למשל היה דורש מאיתנו לכתוב איזשהו מימוש, דיפולטי ככל שיהיה, לשליחת הודעות לשרת והחזרת תשובה, לא היינו משתמשים בו.


אז מה אפשר לעשות?

הדרך הראשונה לפתור את הבעיה שנסקור היא ע”י ירושה מRealProxy. זוהי מחלקה מהFramework, שכפי שרומז שמה, מאפשרת לנו ליצור Proxyים בצורה פשוטה. כל מה שעלינו לעשות זה לרשת מRealProxy ולדרוס את המתודה ששמה Invoke:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyProxy : RealProxy
{
public MyProxy(Type classToProxy) : base(classToProxy)
{
}
public override IMessage Invoke(IMessage msg)
{
IMethodCallMessage methodCall = msg as IMethodCallMessage;
ParameterInfo[] parameters = methodCall.MethodBase.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
Console.WriteLine("{0} := {1}",
parameters[i].Name,
methodCall.Args[i]);
}
// No return value yet (doesn't deal with functions)
return new ReturnMessage(null, methodCall);
}
}

כעת כדי להשתמש בזה פשוט ניצור Instance ונקרא לפונקציה GetTransparentProxy:

1
2
3
4
5
6
7
8
9
10
MyProxy proxyProvider = new MyProxy(typeof(IBank));
IBank bank = (IBank)proxyProvider.GetTransparentProxy();
bank.Deposit("stam", 100);
//id := stam
//amount := 100
bank.Deposit("yossi", 1024);
//id := yossi
//amount := 1024

מגניב ביותר!

אפשר גם לשנות את ערכי ההחזר (כולל הפרמטרים שהם out), ולקבוע Exception וכו’ בעזרת הממשק IMethodReturnMessage (יש טיפוס שנקרא ReturnMessage שמממש אותו שהשתמשנו בו)


איך הדבר הזה עובד?

למעשה יש איזשהו טיפול בCLR שבודק שאם אובייקט מסוים הוא RealProxy, אז מתבצעת הקריאה למתודה לא בצורה הקלאסית שאנחנו מכירים: כל המידע שהועבר לפונקציה בקריאה מועבר לאיזשהו IMessage המכיל את התוכן המתאים, ונקראת הפונקציה Invoke עם הIMessage הנ"ל.

הבעיה העיקרית בשיטה הזו היא שהיא הרבה יותר איטית מקריאה רגילה לפונקציה. אם נערוך איזשהי השוואה נראה כי ההבדלים בין קריאה למתודה ריקה (ללא תוכן) ממחלקה שמממשת ממשק לעומת ממחלקה שיוצרת את הממשק דרך RealProxy הם:

Number of calls Dummy (in milliseconds) RealProxy (in milliseconds)
10 0 0
100 0 0
1000 0 4
10000 0 66
100000 0 549
1000000 9 4927
10000000 99 50100

יוצא שהקריאות של דרך RealProxy הן יותר מפי 500 יותר איטיות מקריאות למתודה.

מבחינת ביצועים זה לא להיט, אבל בהחלט יש שימוש בזה בFramework. עד כדי כך, שאפילו הProxyים שאנחנו מקבלים מChannelFactory של WCF ממומשים באמצעות RealProxy.

שבוע מיופה כוח טוב!

שתף