design patterns - תכנות - HWzone פורומים
עבור לתוכן
  • צור חשבון

design patterns


kutomer

Recommended Posts

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

אז הבעיה היא כזאת:

יש לי class מסוים - נקרא לו provider ולclass הזה יש פונקציה בשם - excuteSheker אשר מקבלת שני פרמטרים:

הראשון string מסוים והשני הוא class שאני יצרתי שמחזיק קונפיגורציה קטנה.

ואני רוצה שברגע שמשתמש יריץ את הפונקציה של הexcuteSheker, אני לפי הקונפיגורציה שקיבלתי יעשה כל מיני פעולות ובסופו של דבר יחזיר כל פעם type שונה (פה למעשה הבעיה - שאותה פונקציה צריכה להחזיר typeים שונים).

חשבתי להשתמש בFactory Method, כלומר כרגע זה הפתרון המועדף עלי - אני אצור class עבור כל אחד מהתוצאות האפשריות (משהו שלמעשה אני כמעט בכל מקרה רוצה לעשות), ואז בעזרת הfactory אני אצור איזה class שבא לי.

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

למעשה מרגיש לי כאילו אני קצת עושה abuse לpattern הזה, כי למשתמש זה אמור להיות שקוף איזה class חזר לו.

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

הFactory שלך צריכה להחזיר קלאס/INTERFACE ששניהם יורשים ממנו, בקלאס אב/אינטרפייס צריכה להיות הפונקציה המשותפת, אם כל פונקציה מחזירה ערכים שונים אז כנראה שזה לא צריך להיות פאקטורי והם לא צריכים לרשת מאותו קלאס/אינטרפייס.

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

אולי אתה רוצה לעשות סוג של builder, הוא גמיש יותר מ static factory. משהו כזה אולי:


builder.setOptionOne();
builder.setOptionTwo();
builder.setOptionThree();


// If each method returns a reference to builder (return this), you can also chain them:
builder.setOptionOne().setOptionTwo().setOptionThree()


// now, just build yourself an object or two:
MyObject obj1 = builder.build();
MyObject obj2 = builder.build();

ולגבי typeים שונים, כמו ש domiel אמר, אם יש להם interface משותף תחזיר אותו. אם לא תשקול משהו כמו composite.

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

factory היא סטטית מבחינת האובייקטים שהיא מייצרת (משתמשת באותם פרמטרים בctor)

builder הוא דינמי, ומייצר אובייקטים לפי הפרמטרים השונים שאתה מספק לו.

Factory מצד אחד נותן אבסטרקציה מלאה בין האובייקט שמיוצר למי שמשתמש בו, אבל פחות גנרי.

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

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

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

ניחשתי שיש לך מספר סוגי אובייקטים שונים לגמרי שנבנים בצורה זהה, במקרה הזה אתה

יכול פשוט לתת אובייקט builder הנה דוגמה מפרוייקט שאני עובד עליו עכשיו:


class INodeTreeBuilder():

def startNode(self, typeName):
raise NotImplementedError()

def addProperty(self, key, value):
raise NotImplementedError()

def closeNode(self):
raise NotImplementedError()

def build(self):
raise NotImplementedError()

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

ולקרוא חזרה. אז פשוט כל פורמט מספק builder (וגם adapter) משלו. ככה נראה הקוד שבונה את העץ:


def buildTree(rootItem, treeBuilder):
_buildTree(rootItem, treeBuilder)
return treeBuilder.build()


def _buildTree(item, treeBuilder):
if item is None:
return

treeBuilder.startNode(item.typeName())
for key, value in item.properties():
treeBuilder.addProperty(key, value)
for child in item.childItems():
_buildTree(child, treeBuilder)
treeBuilder.closeNode()

רואה? שים לב שהוקד שמרכיב את העץ (_buildTree) הוא לא הקוד שבו העץ נבנה.

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

עוד אפשרות: אתה יכול לבנות factory לא סטטי. כלומר אובייקט שבונה סוג של משהו לדוגמה:


class FactoryOfCarFactories
{
CarFactory build();
}

סבבה? עכשיו אני רוצה לבנות מכונית TypeA אז אני יוצר לה פאקטורי:


class TypeACarFactory implements CarFactory
{
TypeACar build();
}

אוקי, אבל מה אם אני רוצה לדעת באיזה פאקטורי להשתמש ב run time וכל מה שיש לי זאת פיסת טקסט?

אז אני יוצר abstract factory שזה factory שיוצר factoryים.





class FactoryOfCarFactories
{
CarFactory build(String factoryName);
}

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

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

שוב, כל זה בתנאי שהם שונים לעומק (יש להם איפשהו הורה משותף). אם הם שונים לרוחב נסה composition .

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

תודה רבה לכולכם...

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

הזכרת בסוף ההודעה שלך את העובדה שזה משנה האם הם שונים לעומק או לרוחב, אז למעשה שני האובייקטים שלי יורשים איזשהו class אב שלי, אבל מה זה משנה כשבc# כל האובייקטים יורשים מobject? למעשה אני יוכל תמיד להחזיר object ותמיד להמיר את זה למה שאני רוצה בסופו של דבר, לא?

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

אני ארדוף אותך עד יום מותך אם תעשה את זה.

מה הפואנטה בפאקטורי אם אתה יודע מה אתה הולך לייצר?

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

object נועד למצבים בהם אתה בכלל לא יודע מה תקבל (SQL), ובכלליות רק כדי לחלוק פונקציות בסיסיות שקריטיות לשפה בכל הקלאסים (getType(), tostring(), gethashcode()), בחיים שלך אל תחזיר object מפונקציה- אם אתה מגיע למצב שאתה צריך- היא לא נכונה.

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

אני ארדוף אותך עד יום מותך אם תעשה את זה.

מה הפואנטה בפאקטורי אם אתה יודע מה אתה הולך לייצר?

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

object נועד למצבים בהם אתה בכלל לא יודע מה תקבל (SQL), ובכלליות רק כדי לחלוק פונקציות בסיסיות שקריטיות לשפה בכל הקלאסים (getType(), tostring(), gethashcode()), בחיים שלך אל תחזיר object מפונקציה- אם אתה מגיע למצב שאתה צריך- היא לא נכונה.

יכול להיות שלא הבנת אותי עד הסוף.

- הclient איננו יודע איזה אובייקט ליצור אז הוא מייצר קובץ קונפיגורציה.

- הserver מקבל קובץ קונפיגורציה לפיו הוא יודע איזה אובייקט ליצור.

- הserver יוצר את האובייקט המתאים ומחזיר אותו לclient כאובייקט אב.

- הclient שמכיר את סוגי האובייקטים הקיימים בודק איזה מהם הוא קיבל ומשתמש בו בהתאם.

אני יודע, זה עקום ממש, כי מצד אחד אני עושה factory מצד שני אני מחזיר אותו חזרה - אז כאילו למה לעזאזל אני עושה את זה?

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

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

לבנות בחלקים זה בדיוק כמו שזה נשמע, תחשוב XML שניה... אוקי נגיד ויש לי 2 אובייקטים

אחד בתוך השני, ככה הקריאות שלי לבילדר יראו:


import sggle.saveload.formats._xml as xml




builder = xml.builder()


builder.startNode("ParentItem")
builder.addProperty("name", "zero")
builder.addProperty("id", 0)
builder.startNode("ChildItem")
builder.addProperty("name", " one")
builder.addProperty("id", 1)
builder.closeNode()
builder.closeNode()


xmlTree = builder.build()
xmlTree.dump()

זה יהיה הפלט:




<?xml version="1.0"?>
<ParentItem>
<properties>
<name>"zero"</name>
<id>0</id>
</properties>
<children>
<ChildItem>
<properties>
<name>"one"</name>
<id>1</id>
</properties>
<children/>
</ChildItem>
</children>
</ParentItem>

שים לב שאני : [פותח ממלא פותח ממלא | סוגר סוגר] הבנת?

הזכרת בסוף ההודעה שלך את העובדה שזה משנה האם הם שונים לעומק או לרוחב, אז למעשה שני האובייקטים שלי יורשים איזשהו class אב שלי, אבל מה זה משנה כשבc# כל האובייקטים יורשים מobject? למעשה אני יוכל תמיד להחזיר object ותמיד להמיר את זה למה שאני רוצה בסופו של דבר, לא?

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

לכתוב ifים + castים אם אתה רוצה לעשות משהו מועיל... תחשוב על זה שניה, factory שמחזיר לך את האובייקט

הנכון כבר עשה את כל ה ifים האלה בעזרת הורשה ב compile time.

התכוונתי ל"יש להם interface משתוף", יש כלל זהב כזה בהנדסת תוכנה שאומר:

"program to an interface not an implementation"

כלומר לא אכפת לי מה זה העיקר שזה מכין לי קפה!

או... תפריד את המה מהאיך.

נגיד ויש לך מפעל לעבדי קפה, מה כל עבד יודע לעשות? לספק לי קפה כמובן!

יופי, זה ה interface שלך. איך הם עושים קפה לא חשוב לי, כשאני coffeeNow() אותם אני

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

מצדי שהיו ZombieCoffeeSlave או CoffeeMachine כל עוד אני יכול coffeeNow() אותם!

יש מבין?

יכול להיות שלא הבנת אותי עד הסוף.

- הclient איננו יודע איזה אובייקט ליצור אז הוא מייצר קובץ קונפיגורציה.

- הserver מקבל קובץ קונפיגורציה לפיו הוא יודע איזה אובייקט ליצור.

- הserver יוצר את האובייקט המתאים ומחזיר אותו לclient כאובייקט אב.

- הclient שמכיר את סוגי האובייקטים הקיימים בודק איזה מהם הוא קיבל ומשתמש בו בהתאם.

רואה את הבעיה פה? תחשוב על קפה :)

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

ואו, קודם כל תודה רבה על התגובה הזאת - החכמתי. :yelclap:

הבנתי את המשמעות של builder וכנ"ל של factory אך עדין אני נתקל בשני המקרים באותו הבעיה -

יש לי טיפוס אב -

 
public Query
{
proprties and members
}

ויש לי 2 טיפוסים שיורשים ממנו -


public RegexQuery : Query
{
string ExcuteQuery()
}



public ConditionalQuery : Query
{
List<reference> ExcuteQuery()
}

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

בסופו של דבר אני מניח שהייתי מעדיף שהמשתמש לא היה מכיר את סוגי הטיפוסים האלו, אלא פשוט מכניס את כל הפרטים הרלוונטיים אליו מריץ ExcuteQuery ומקבל את הטיפוס המתאים - אך בפועל אם הוא לא מכיר את האובייקט פיזית איך הוא ידע איזה ערך חוזר לו?

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

כלומר גם ברגע שאני מקבל את הquery הנכון אני לא יכול להריץ בו את הפונקציה הזו כי היא פשוט לא קיימת שם.

ושוב... תודה :xyxthumbs:

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

אני יכול להחזיר טיפוס של QueryResult שמחזיק את כל סוגי ההחזרה האפשריים ולצרף לו פונקציה שתבדוק מה מהם בשימוש, זה באמת טוב יותר?

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

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

אני יודע, זו בדיוק הבעיה ולכן אני שואל איך אני יכול לשפר את זה?

שני הטיפוסים RegexQuery ו ConditionalQuery דומים מאוד, הם למעשה כמעט וזהים למעט כמה שינוים קטנים בפונקציה excuteQuery שפשוט מחזירה כל פעם טיפוס שונה (למעשה במקרה הזה דווקא שני הטיפוסים האלו מחזירים שניהם את אותו טיפוס, אבל יש לי גם FullQuery ValuesQuery שכל אחד מהם באמת מחזיר טיפוס שונה)

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

ארכיון

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

×
  • צור חדש...