new .VS. virtual&override

orenphp

New member
new .VS. virtual&override

אהלן. אני לא זוכר אם כבר ניהלנו את השיחה הזאת (אם כן, לא הצלחתי למצוא אותה), אבל אני מתלבט קשות לגבי הארכיטקטורה אצלנו בצוות. כיום אני מחולל קוד(בדוט-נט) אשר דואג לבנות את הבסיס הדרוש לצוות ולי לצורך הפיתוח. עד כה, כמעט כל מחלקה שלי הייתה virtual ונתנה לי בעצם את החופש לדרוס אותה במחלקה היורשת ולשנות את ההתנהגות שלה. חשבתי על זה לא מעט היום, לאחר שקראתי מספר מאמרים בנושא, וחשבתי להוריד את הvirtual ובמידה ואני צריך שינוי במתודה/מאפיין - אני פשוט ארש מן המחלקה ואשתמש במילה new לצורך הגדרה מחדש ו"העלמה" של ההגדרה הישנה. מבחינת איך שאני מבין את new, ניתן להשתמש בו לצורך ספסיפקציה וזה בדיוק מה שאני מחפש בדריסה של הבסיס. האם עצם העובדה שאני כותב ליד כל מתודה/מאפיין virtual מאיט את הביצועים ? האם virtual + overide או פחות נכון מאשר המילה new (מבחינת עיצוב) ?
 

ייוניי

New member
הבעיה אם new

היא שאם אתה עושה boxing לאובייקט אתה תקבל שימוש במתודות/מאפיינים של מחלקת האם ולא מחלקת הבת. אם אתה לא משתמש ב boxing (הצבה של אובייקט למשתנה של מחלקת האם) לא צריכה להיות לך בעיה לשתמש ב new. לגבי ביצועים אני לא סגור על זה אבל נראה לי שביטול קריאות וירטואליות בקוד שהוא גם ככה מנוהל זה קצת מצחיק...
 

orenphp

New member
בדיוק הטיעון הזה מחזק את דעתי

בנוגע לnew. כלומר, במחלקות כמו EmployeesDAL או EmployeesBO, כאשר אני ארצה לדרוס מתודה או מאפיין, נראה לי תקין שזה יהיה new כי אני לעולם לא אעבוד עם האבות שלהם באופן ישיר (אולי דרך Interface, אבל גם אז אין לי בעיה אמיתית). איפה שיש חשיבות רבה לעניין הדריסה אני אנקוט בvirtual או בabstract (תלוי במקרה). אצלך בקוד - אתה מבצע שימוש בnew או שאתה משתמש בעיקר בoverride-ים ?
 

ייוניי

New member
לא זה ולא זה ../images/Emo13.gif

מאחר ואני לא משתמש בהורשה אלא רק בישום ממשקים... אבל אם כבר אז הייתי משתמש ב abstract משום שכל אובייקט שאני יוצר עובר boxing מיידי ולמעשה אני לעולם לא משתמש בהפניה למחלקה האמיתית (מלבד שלב יצירת האובייקט כמובן). הרעיון המרכזי ב OOP הוא לכתוב קוד אבסטרקטי שעובד מול ממשק מסויים בלי לדעת מהו היישום הספציפי של אותו ממשק. אם אתה עובד תמיד עם המחלקה האמיתית הרי כל הקוד שקורא למתודות על המחלקה הזו אינו ניתן לשימוש חוזר באופן OO. (אתה יכול לקרוא לקוד הזה ממקומות שונים ולשלוח מופעים אחרים של המחלקה אבל זה כבר תכנות פרוצדוראלי ולא ניכנס לבעייתיות שבו).
 

orenphp

New member
צודק אבל לדעתי -

מי רוצה לעבוד בכלל עם OOP טהור כל הזמן ? המשפט שאני תמיד אומר הוא - "לכל דבר יש את הtradeoff שלו" ובפיתוח של אפליקציות IT, אני לא גורס בדעה שפיתוח OOP טהור באמת יקל לי על תחזוקה, יקצר זמני פיתוח וכו'. אם כבר להפך - אני דווקא חושב שפיתוח OOP "לא טהור, אבל לא רע" באמצעות data driven development שעל זה מבוסס המחולל קוד שלי יתן כמעט תמיד תוצאות יותר טובות, מכל הבחינות, מאשר OOP טהור. כמובן ש"על טעם ועל ריח" מעניין להתווכח, אבל זה לא המטרה של השרשור. הייתי יותר שמח אם היית מנסה להבין שזה הצורך שלי בעבודה ולהגיד לי את דעתך כאשר אתה לוקח בחשבון שאני עובד בשיטה שבה יש לי מחלקה שאני מחולל ומחלקה נוספת שיורשת ממנה ובה יש את הקוד הידני שהתוכניתן כותב. אני יודע, אני גם לא כ"כ אוהב לענות על שאלות כאלו כאשר הדחף המיידי שלי הוא - "אתה עובד לא נכון, בוא תנסה להסביר לי מה אתה רוצה להשיג ואז אני יגיד לך איך אני הייתי מממש את זה". תנסה לתת לי רעיון/פיתרון מתוך השיקול שזו צורת פיתוח שונה שמוכיחה את עצמה הרבה שנים מבחינתי.
 

ייוניי

New member
אם כך

תראה אני פשוט יוצא מנקודת הנחה שהתעסקות עם הורשה ו overriding היא נסיון להתעסק ב OOP ולא ב Procedural. אם אתה רואה יתרון בתכנות Data Driven אז אני לא מבין למה לך להתעסק עם הורשה בכלל? אם ההורשה היא לא למטרת פולימורפיזם אז מה המטרה? בתכנות Data Driven אין לך הרי שימוש באובייקטים במובן ה OO של המילה, מה שיש לך זה ספריות של פרוצדורות (בד"כ נקראות מודולים) ומודל מידע שכל פרוצדורה יכולה לגזור ממנו נתונים ולעבוד איתם. אם אני מתרגם את זה ל NET. למשל אז מדובר במספר רב של מחלקות סטאטיות לחלוטין שביניהן מועברים מבני נתונים שונים ומתבצעים עליהם פעולות שונות. השימוש בהורשה כאן פשוט נראה לי קצת מיותר ומבלבל. לדעתי בהורשה אתה תמיד יוצר מצב שבו תידרש לזהירות רבה כאשר אתה נוגע בקוד של מחלקת האב ואני לא אוהב ליצור לעצמי מצבים מסוכנים. אני חושב שאם תעבור לשיטה של העברת מבני נתונים בין פרוצדורות ללא הורשה תקבל פחות מחלקות (פחות קוד זה תמיד טוב), קוד קריא יותר (בלי מתודות בלתי נראות שלא ברור איזה אב הגדיר אותן) ואפשרות לעקוב בקלות רבה יותר אחרי זרימת התוכנית ולשנות אותה. אני מוכרח להודות שאני יחסית חלש בתכנות Data Driven כי זנחתי אותו כבר די מזמן אבל אני בטוח שאם תשאל את המומחים הגדולים בתחום הם יאמרו לך ש DD ו-OO לא מתחברים ואין מקום להורשה בתכנות פרוצדוראלי.
 

ייוניי

New member
ולגבי "מי רוצה OOP טהור"

אני לא יודע מה להגיד לך אבל מה שאני רואה בשוק זה מספר עולה ועולה של שפות OO טהורות (כולל #C) וזניחה די שיטתית של שפות וכלים פרוצדוראליים או שאינם OO טהור, לפחות בתחום ה Enterprise Applications שמתעסק באפליקציות גדולות. מסכים איתך שהיתרון התחזוקתי של OOP מצטמצם לעומת Data Driven באפליקציות קטנות ושאינן דורשות תחזוקה רבה אבל עולם ה IT מתחיל להתכחש לקיומן של אפליקציות כאלו עם העליה במורכבות של מערכות מידע וההבנה שתחזוקה היא החלק המשמעותי ביותר במחזור החיים של האפליקציה. אני יכול להפנות אותך למספר רב של ספרים שנכתבו על ידי ותיקים בתחום וזכו להצלחה עולמית, אשר עוסקים ב OOP טהור ורואים בו שיטה מוצלחת מאוד...
 

Fractal

New member
אולי אתה מכיר, אבל אם לא...

http://en.wikipedia.org/wiki/Aspect-oriented_programming כמו שאני מקווה שתראה, OOP טהור זה ממש לא סוף הדרך
 

ייוניי

New member
כמו שאתה מבין מהטקסט

AOP הוא נסיון (לדעתי לא מספיק ברור) להתמודד עם בעיה של קוד שחוזר על עצמו שנובעת מחוסר הבנה מעמיקה מספיק של OOP. הטעות היא נקודת ההנחה שאלמטים כגון Logging, בדיקות תקינות קלט ו Database Connectivity הם אספקטים שחוזרים על עצמם במקומות רבים בקוד. ברגע שאתה כותב מחלקה שמבטאת "לוגיקה עסקית" טהורה שהיא אבסטרקטית לחלוטין ומבודדת מקיומו של DB, מצרכי ניטור וכיו"ב - לא צריכים להיות בה את אותם אספקטים ש AOP מנסה לבודד. אותם אספקטים צריכים לבוא לידי ביטוי (לפי תפיסת OO) אך ורק במחלקות Low Level העוסקות בדבר. בקיצור, שימוש נכון ב Command Pattern יכול לפתור הרבה מאוד מהבעיות ש AOP מנסה לפתור באמצעים די מורכבים לטעמי. למען האמת AOP איננו אלטרנטיבה אמיתית לצורות תכנות אחרות אלא בא כתוספת להן.
 

Fractal

New member
לדעתי זה צעד נוסף בכיוון הנכון.

AOP בא להתמודד לא רק עם בעיה של reuse, הוא בא להתמודד גם עם בעיות של אופטמיזציה לדוגמא, ע"י טרנספורמציה של הקוד. מכיר את זה שאתה מסיים לכתוב את האפליקציה, מריץ profiler, ואז מתחיל לשנות את הקוד היפה שכתבת ? אם ניקח logging לדוגמא, איך בדיוק אתה יכול להימנע מקריאות ל-logger במודולים שונים ? (אתה צריך לקרא ל-command איפשהו, לא ?) AOP בא, בין היתר, "לנקות" את הקוד מכל הקריאות למינהלות האלה. מסכים עם המשפט האחרון, כמו ש-OO טהור בא כתוספת לתכנות פרוצדואלי - קריאות למתודות.
 

Justin Angel

New member
../images/Emo26.gif

הרעיון ב-new הוא לשבור את שרשרת הירושה. בזמן ש-virtual&override שומרים על אותה שרשרת ירושה. ניתן דוגמה:
public class BaseClass { public BaseClass() {} public virtual void PrintHelloWorld() { Console.Writeline("Hello"); } } public class OverrideClass { public BaseClass() {} public override void PrintHelloWorld() { Console.Writeline("my"); } } public class NewClass { public BaseClass() {} public new void PrintHelloWorld() { Console.Writeline("world"); } }​
עד כאן פשוט וישר. ההבדלים מתגלים בפולימורפיזם:
BaseClass myBaseClass = new BaseClass(); myBaseClass.PrintHelloWorld(); // will print "hello" BaseClass myBaseClass = new OverrideClass(); myBaseClass.PrintHelloWorld(); // will print "my" BaseClass myBaseClass = new NewClass(); myBaseClass.PrintHelloWorld(); // will print "world"​
הבעיה היא ש-New שברה את שרשרת הירושה בין המחלקות השונות ובאמת לא מאפשרת עבודה ב-OOP כמו שצריך. לא משתמשים ב-new כחלק מתכנון של שום דבר. new הוא רק למקרה ואתה צריך משהו מהיר ומיד כדי שהקוד יעבוד. אגב, בכלל לא בריא סתם לג'נרט Virtual. זה יוצר קוד יחסית לא-קריא. כי אי-אפשר לדעת בקריאה רגילה אם באמת מישהו דורס את ה-Member במחלקה או לא וזה יוצר מצב של קושי אדיר בתחזוקת המחלקה. עדיף תמיד להוסיף Virtual רק איפה שיודעים שצריך (ואפילו אם זה רק כמה שעות\ימים לפני).
 

orenphp

New member
קצת חבל על הדוגמא, אבל תודה על

הטרחה, אני מכיר את הנושא מאוד לעומק. אני לא מסכים שnew שובר את יכולת העבודה שלי ב-OOP אם כבר להפך, הוא מאפשר לך ספיפקציה כאשר אתה עובד ישירות מול reference של ה"בן"/המחלקה הרצוייה. לגבי חילול הקוד עם virtual - למעשה, אין לי מנוס כיום (אלא אם אני אתחיל לעבוד עם new), אלא להגדיר את המתודות שלי כvirtual כי אני עובד עם שני מחלקות -
public class EmployeesDalBase { // generated code here. } public class EmployeesDal : EmployeesDalBase { // nothing here except CUSTOM implementation. }​
המחלקות עם ה"Base" מחוללות אוטומטית וכל שכבה למעלה עובדת רק עם המחלקות ללא ה"Base". זה מאפשר לי את הכוח לשנות עכשיו מתודה (למשל GetAll) ואז כל הלקוחות שלי יושפעו מזה ובנוסף, בחילול הבא שלי, אני לא אאבד את השינוי כי אני לעולם מחולל את הBase. עכשיו אני חושב על כך שבמקום לכתוב virtual, מי שירש ממני (EmployeesDal) וירצה לדרוס מתודה כלשהי, פשוט ישתמש בnew - גם ככה אין לו סיבה לעבוד עם האבא, EmployeesDalBase, כי תמיד עובדים עם הבן - EmployeesDal. זה יתן לי את יכולת הספסיפקציה כמו שאני רוצה - כלומר היכולת להגיד לDal הספציפי איך הוא צריך להתנהג. אם אני עדיין ארצה לעבוד בצורה יותר אבסטרקטית - אני אשתמש בinterface (IDAL) ודרכו אני אוכל להנות עדיין מהnew שביצעו. דעתך ?
 

Justin Angel

New member
שאלה

למה בכלל למחלקות שלך יש הירכיה של ירושה? הרי יש סיבה שאתה כותב את הקוד הזה, מהי? מה הפונקציונליות או הגמישות שאתה מנסה להשיג בכתיבת קוד כזה? new כן שובר הירכיית OOP. זה התפקיד שלו. עבודה עם new דורסת את ה-Members ממחלקת האב ושוברת את שרשרת הירושה. אם בכל מקום בתוכנית שלך תכתוב new במקום virtual&override תקבל שלא תוכל לבצע שום פולימורפיזם באפליקציה.
 

orenphp

New member
הסברתי בקוד שצירפתי

אני מחולל שני מחלקות - האחת מחוללת בלבד והשנייה "קוד ידני של תוכניתן" בלבד. ככה אם אני צריך לחולל מחדש, כל השינויים שנעשו ע"י התוכניתנים לא נדרסים. new שובר באמת היררכית OOP, אבל זה חלק מהכוח בו ועם כל יתרון החסרון שלו. אני לא אוכל לבצע פולימרופיזם במחלקות הספציפיות הללו, שגם ככה איך שאני רואה אותם לא יהיה בהם פולימרפיזם אמיתי (לפחות בצורת עבודה שאני עובד), אלא אם כן אני אשתמש בinterface-ים במקום בreference ל"אבא" שלהם. כלומר בהחלט יש לי אפשרות לעבוד עם פולימרופיזם מלא, אני פשוט אצור מערך (לצורך העניין) מסוג interface ולא מסוג אחד האבות. מה שכן, ברוב המקרים אני לא עושה override ולכן אני מתלבט אם בכלל יש צורך להשתמש בvirtual כdefault.
 

hg1979

New member
הממ

אתן תשובה רק על סוף השאלה שלך (כי Justing angel נתן תשובה דיי טובה לשאר הנושא), כן זה מאט את הביצועים ברוב המצבים - שימוש נכון בפולימורפיזם מבטל כמעט את ה"עלות" בביצועים, רק בקצרה, עבור כל רמה של מחקלה בעץ ההורשה הוא שומר טבלה של מיפוי המטודות, אם בכל רמה יש Override אז עומק המיפוי גדל בצורה לינארית, במצב הטוב צריכים לכתוב מימושים בכמה שפחות רמות הורשה.
 
למעלה