מימוש פולימורפיזם ב-C# במקום switch או if - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

מימוש פולימורפיזם ב-C# במקום switch או if


Avi.Sprngr

Recommended Posts

שלום,

בקטע קוד המצורף אני מעוניין לקרוא מהמשתמש ערך אחד וכך להפעיל את המחלקה הרלוונטית במקום המערך הנוכחי.

כיצד אני עושה זאת?

http://www.csharp-station.com/Tutorial/CSharp/lesson09



using System;

public
class DrawingObject
{
public virtual void Draw()

{
Console.WriteLine("I'm just a generic
drawing object.");
}
}


using System;

public
class Line : DrawingObject

{
public override void Draw()

{
Console.WriteLine("I'm a Line.");

}
}


public class Circle : DrawingObject
{
public
override void
Draw()
{
Console.WriteLine("I'm a
Circle.");
}
}

public class Square : DrawingObject

{

public override void Draw()
{

Console.WriteLine("I'm a Square.");
}

}


using System;

public
class DrawDemo
{
public static int
Main( )
{
DrawingObject[] dObj
= new DrawingObject[4];


dObj[0] = new Line();

dObj[1] = new Circle();

dObj[2] = new Square();

dObj[3] = new DrawingObject();


foreach (DrawingObject drawObj
in dObj)
{

drawObj.Draw();

}

return 0;

}
}

קישור לתוכן
שתף באתרים אחרים

הפולומורפיזם כבר שם. אם היית כותב את זה בדרך הלא פולימורפית. הייתה לך מחלקה אחת ו switch בתוכה.

תראה, פולימורפיזם מאפשר לך (בין השאר) לקחת את ה ifים שלך מזמן הריצה לדחוף אותם לזמן הקימפול ע"י

שימוש בהורשה. תחשוב על הורשה כסוג של משפט if שהקומפיילר שלך מריץ.

הבעיה היא כשיש לך משהו כמו קלט משתמש אתה לא יכול להעביר את התנאי לזמן קימפול. כי אין לך את המידע (קלט)

שאתה צריך בזמן קימפול! לכן איפשהו ואיזשהי צורה יהיה לך בקוד משהו כזה:



DrawingObject createShapeFromUserInput(UserOption option)
{
if (option == UserOption.Line)
return new Line();


if (option == UserOption.Circle)
return new Circle();


if (option == UserOption.Square)
return new Square();
}

פה זה factory method וזה בסדר גמור. כי שים לב מה הפונקציה מחזירה.

הטריק הוא לבודד את החלקים האלה לתוך פיסות שקל לנהל.

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


DrawingObject nextEnemyShape = createEnemyShape(optionFromMenu);

(זה רע כי אתה כל פעם מריץ את ה ifים האלה)

אתה יכול ליצור אובייקט שמייצר אובייקט שמייצר אובייקטים קוראים זה abstract factory .

שים לב ש abstract factory זה משחק מילים די טריקי, אבל אם תחשוב על זה אין שום דבר מורכב.

הנה דוגמה:

-> יש לך 3 מכונות: X יודעת לעשות כדורים, Y יודעת לעשות ריבועים ו Z יודעת לעשות משולשים

-> ויש לך מכונה מגניבה בשם W שיודעת לייצר מכונות לפי דרישה, לדוגמה אם אתה אומר כדורים היא תייצר מכונה X.

-> אז X, Y , ו Z הם factory כי הם מייצרים צורות. ו W היא abstract factory כי היא מייצרת מכונות שמייצרות צורות.

מקווה שזה לא מבלבל יותר מדי... :)

קישור לתוכן
שתף באתרים אחרים

למעשה אני לא מחליף את ה-SWITCH ע"י פולימורפיזם אלא פשוט דוחף אותו אולי למקום אחר.

ניתן להבין ממאמרים שונים בנושא שע"י פולימורפיזם ניתן להוסיף מחלקות ולהטמיע בהן את מחלקת הבסיס וכך למעשה להוסיף עוד

"CASE" אחד לתוכנית מבלי לחפש את ה-SWITCH המקורי בגוף הקוד ולשנות אותו, אם הבנתי נכון זה בלתי אפשרי, תמיד

יהיה צריך לחפש ולהוסיף עוד תנאי בתוך הקוד.

קישור לתוכן
שתף באתרים אחרים

כן ולא.... אתה חייב לפחות SWITCH* אחד כשזה קלט. הבנת למה?

אבל סתכל על הדוגמה עם abstract factory , תראה שגם אם אתה יותר מליון אובייקטים ה switch רץ רק פעם אחת.

וגם זה שומר על הקוד נקי יותר, וזה שימוש יפה בהורשה ופולימורפיזם.

* טוב טכנית אתה יכול להשתמש ב map עם הקלט כמפתחות וה factories כערכים, אבל זה אותו דבר.

עריכה:

אם הבנתי נכון זה בלתי אפשרי, תמיד

יהיה צריך לחפש ולהוסיף עוד תנאי בתוך הקוד.

אם אפשר לפתור את התנאי בזמן הקימפול אתה יכול להשתמש בהורשה, אם לא אז אתה צריך סוג של if איפשהו.

סתכל, בקוד שהבאת אני יכול לעשות ככה:

  public virtual void Draw()
{
// crappy code
if (type == UserOption.Line)
Console.WriteLine("line");
else if (type == UserOption.Circle)
Console.WriteLine("Circle");
else if (type == UserOption.Square)
Console.WriteLine("Square");
....

}

וזה חרא של קוד. ודוגמה מצויינת למצב שבו אתה כן יכול להשתמש בהורשה במקום if .

קישור לתוכן
שתף באתרים אחרים

אם אני לא טועה אפשר לעשות את זה בעזרת Reflection.

לבנות אובייקט Type לפי שם ואז להפעיל את מותדת ה- Draw.

החסרון של Reflection הוא שהוא יחסית איטי אבל לא נראה לי שזה רלוונטי עבורך פה.

קישור לתוכן
שתף באתרים אחרים

לגבי reflections.

תחשוב על זה שניה, הרי לא יהיה לך מיפוי 1:1 בין הקלט לשם המחלקה, לכן ה ifים עדין שם.

וגם אם תעשה אחד, איך תדע מה האפשרויות שלך? כלומר, מה ימנע מהמשתמש שלך להכניס שם של מחלקה שאתה לא רוצה ?

כן, אתה יכול לבדוק אם המחלקה יורשת ממחלקה כלשהי, אבל מה אם כל מה שיש לך לעבוד איתו זה interface ?

כמו למשל בדוגמה הזו, מה אם יש לך בקוד:

DebugDrawingObject implements DrawingObject

אתה צריך לעשות וולידצה על הקלט נכון? ופה ה if שלך מסתתר :)

כן, אתה יכול לסדר את ה packageים בצורה "חכמה". אבל לא עדיף כבר לשמור רשימת אובייקים בקובץ XML או json ?

ואז זה עדין אותו if, רק המידע נמצא במקום אחר... וכל מה שאתה "מרוויח" פה זה את זמני הקמפול, בזמן שאתה מפסיד

את הוולידציה שהקומפלייר עושה, ועכשיו הקוד שלך צריך לעשות וולידציה בזמן ריצה.

מה שאני מנסה להגיד הוא שצריך להיזהר עם reflections . הייתי חושב 70 מליון פעם לפני שהייתי משתמש

ב reflections ישירות לקלט, ובוודאי שלא הייתי חולם להשתמש בהם כפתרון סטנדרטי.

reflections סך הכל מאפשרים לך להפוך שפה סטטית לדינאמית. אם משתמשים בהם יותר מדי מקבלים

את הרע משני העולמות (מנסיון :) ).

קישור לתוכן
שתף באתרים אחרים

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

×
  • צור חדש...