אז איך אני עובר לאובייקטים?

אלחנן03

New member
אז איך אני עובר לאובייקטים?

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

ייוניי

New member
עוברים לאובייקטים

תשובה טובה לשאלה שלך תהיה - "תתחיל ללמוד". יש המון ספרים טובים ומשאבים ברשת בנושא. (בתקווה נבנה כאן בפורום מאגר קישורים טוב בנושא). אז תאלץ להסתפק בתשובה "על קצה המזלג": אני לא יודע למה אתה מתכוון ב"עצם" שיש באפיון אבל ההגדרה הכללית של אובייקט היא רכיב שמגלם התנהגות מסויימת או באופן כללי מאוד - "יודע לעשות דברים". זה שונה מאוד מההגדרה של ישות במובן הרלציוני שהיא - "מכילה מידע מסויים". והכי טוב להבין את העניין הזה על ידי הסתכלות על קוד:
public class BankAccount { long _balance; public long getBalance() { return _balance; } public void setBalance(long newBalance) { _balance = newBalance; } } public class Test { public static void withdraw(BankAccount account, long ammount) { account.setBalance(account.getBalance() - ammount); } }​
הגישה הפרוצדוראלית מתעסק ביחידות המכילות מידע ופונקציות המבצעות עבודה על המידע. בגישה ה Object Oriented אין חלוקה כזו אלא יש רק רכיב שיודע לבצע פעולות. קיום המידע בתוך הרכיב איננו ידוע למשתמשים בו ולכן קל מאוד לשנות את מבנהו בלי להשפיע כלל על שאר המערכת.
public class BankAccount { long _balance; public void withdraw(long ammount) { _balance = _balance - ammount; } } public class Test { public static void withdraw(BankAccount account, long ammount) { account.withdraw(ammount); } }​
עכשיו אין לי בעיה לשנות את BankAccount כך שפעולת ה withdraw תקרא ל WebService של בנק מסויים במקום לעדכן ערך, והשינוי לא ישפיע על המערכת בגלל שלא חשפתי את מבנה הנתונים של האובייקט. (מה שיהיה הרבה יותר מסורבל ולעיתים בלתי אפשרי לעשות במודל הפרוצדוראלי). עוד לא נגעתי בהורשה אבל ברגע שאתה מתרגל לרעיון ה"רכיב שמבצע התנהגות" במקום "רכיב שמכיל מידע" אתה מהר מאוד מגיע לצורך לבצע התנהגות בצורה דומה אך לא זהה במקומות שונים. את זה אפשר לבצע בשתי דרכים: 1. להגדיר את האובייקט באופן יותר אבסטרקטי (להשתמש ב interface) וליישם אותו בעזרת מספר מחלקות שונות. 2. לדאוג שהאובייקט יתפקד אחרת בהתאם למידע שברשותו (למשל שאם ה balance קטן מ-0 לשלוח הודעת אזהרה...).
 

עידו פ

New member
את השלב הראשון אתה יודע

אם האפיון אינו בגישת OO ואינו מזהה מראש את הישויות במערכת, הדרך הטובה לזהות את הישויות היא לפי שמות העצם באפיון - ואת זה ביצעת. הורשה זה משהו שצריך להיות מובן מהטקסט (מהאפיון). הורשה יכולה להיות בין ישות אחת לאחרת אם הישות היורשת מרחיבה את היכולות של הישות הקודמת שזה לרוב קורה כאשר הישות ממנה יורשים היא ישות כללית והישות היורשת היא ממוקדת יותר (לדוגמה - אדם שממנו יורש עובד, רכב שממנו יורש רכב משפחתי, מסמך שממנו יורש מסמך וורד). ההחלטה מתי לרשת ומתי לא לרשת היא החלטה של נסיון ושל הבנה (הטעות הרווחת היא הורשה בגלל תכונות משותפות ללא קשר לפעולות אותה הישות מבצעת). דרך טובה לחלק תכונות ופעולת בין מחלקות (ישויות) היא באמצעות שיטת ה-CRC Cards אבל שיטה זו דורשת עבודה בקבוצה (בגלל האופן בה היא מבוצעת) ואני לא יודע אם זה אפשרי במקרה שלך (אם תרצה, אני ארחיב). אני אשמח אם תוכל להרחיב על "בונה גם מחלקה אחת גדולה", מה הכוונה ? כי לרוב, כאשר מדברים על אפיון/עיצוב מונחה עצמים (OO) אמורים לא לקחת בחשבון את הצורה בה ישות שומרת את עצמה ב-DB (מה שמכונה בעגה המקצועית persistence) - ישות אמורה לדעת לשמור את עצמה ולטעון את עצמה (לא אמורה להיות מחלקת DB שמגשרת בין הישויות לנתונים שלהן). אם תרחיב קצת על איך אתה עושה את זה, נוכל לתקן אותך (אם אכן ביצעת זאת לא נכון) ולכוון אותך.
 

אלחנן03

New member
לרוב אני בונה מחלקה אחת

אשר כל המחלקות האחרות משתמשות בה כדי לשלוך נתונים מתוך מסד נתונים... כך ששום מחלקה עקרונית לא צריכה לשלוח sql ישירות.. אל רק לקרוא לשיטה. אין לי crc (לא עובד בצוות)
 

ייוניי

New member
זו התחלה טובה

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

אלחנן03

New member
במחלקה זו המתודות מאוד קטנות..

אבל הרבן מהן. סה"כ יש 2-3 מתודות בסיסתיות בתוך המחלקה שממש מאלכסות נתונים, וכל שאר המתודות במחלקה משתמשות בהן עם משפטי sql. במחלקות אחרות יש לי מתודות עם יותר מ-20 שורות קוד.
 

עידו פ

New member
איך אתה מחלק את העבודה בין המחלקות?

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

אלחנן03

New member
הסגנון האחרון...

אני עובד כעת בnet.. (מקודם בדרך כלל הvb6 שם זה לא היה אפשרי בכלל).. במחלקה הענקית יש 2-3 מתודות בסיסיות (אחת שמקבלת משפט sql, אחת שמכינה OracleCommand).. וכל שאר השיטות משתמשות בtyped DataTable כדי לאכלס אותן) דוגמא: private void FillDt (DataTable dt,string sql) { OracleDataAdapter o =new OracleDataAdapter(conn,sql) o.fill(dt) return dt; } public DataTable GetClients(int client)( { string sql="select * from client where clientno=client"; ClientDataTable dt = new ClientDataTable() return FillDt(dt,sql); }
 

ייוניי

New member
-->

תראה מה שיש בדוגמא נראה די פשוט בעקרון (למרות שהקוד שם לא מתקמפל אבל הבנתי את הרעיון) - ופשוט זה טוב. הייתי מציע לך להוסיף לכל המתודות את הקידומת static ולקרוא להן בלי לעשות new למחלקה (חוסך זכרון). ואם המחלקה מתחילה לגדול מדי אולי עדיף לפזר את המתודות בין כמה מחלקות עם שמות מתאימים. (וגם הייתי הופך את FillDt ל public כי הפרטיות שלה לא עוזרת לאף אחד...) אבל האמת היא שהעסק פה הוא עדיין עם מחלקות ולא עם אובייקטים. אולי עדיף שתיתן דוגמא למחלקה אחרת (כזאת שיש לה מתודות גדולות) שעושה שימוש במחלקה הזו ונוכל לראות אם יש מקום לשיפור בעיצוב שלה.
 

אלחנן03

New member
אז זהו שבגירסה קודמת זה סטטי..

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

ייוניי

New member
connection לא יכול להיות סטטי

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

עידו פ

New member
אם הייתה לי אפשרות, אני הייתי

מחלק את זה כך שהמחלקה שניגשת ל-DB היתה יודעת רק לגשת ל-DB ולהפעיל את השליפות ואילו כל ההגדרות של מה לשלוף היו יושבות במתודות במחלקות של הישויות. בצורה זו, המחלקה שניגשת ל-DB היא מחלקה שמספקת תשתית למערכת ולא מחלקה המספקת לוגיקה ששייכת לאחת מהישויות. בנוסף לעיל, השימוש שעשית ב-* במשפט ה-select הוא לא הכי מומלץ (בוא רק נאמר שב-8 השנים האחרונות בהם אני עוסק בפיתוח, בכל פרויקט המנטרה חזרה על עצמה - * זה רע !). למה לא טוב ? כי אתה לא בטוח מה אתה הולך לקבל (כמה עמודות, באיזה סדר) ולהריץ קוד שלא ברור לך מה הוא מחזיר - זה אף פעם לא טוב. ומעבר לכך, מה לגבי העברת כל השליפות ל-DB והגדרתם כ-SP ? אני אישית בעד (אף על פי שפעם הייתי נגד), אבל אני אשמח לשמוע דעות של אחרים.
 

אלחנן03

New member
בעיה שאני לא רוצה לשים sql

במחלקה חיצונית, מה יקרה אם אצטרך שוב אותו sql? בדרך בבסיס נתונים עצמו הגדרתי views ורק אליהם אני ניגש בעיקר, כך שלעיתים אני צריך לשנות רק view. אני יודע ש* זה דבר רע, בדרך כלל אני משתמש בו לטבלאות קטנות), פשוט לא היה לי חשק להתחיל לרשום רשימת שדות, עוד שהקוד עצמו לא קריא כאן. ב-sp אני משתמש בעיקר שיש לי נתונים לעדכן, או שיש לי מספר שליפות ועידכונים ברציפות אני יכול לעשות את כולם בsp אחד, כך שהנתונים לא צריך לרוץ בין הdb לאפליקציה ברשת. מעולם לא הייתי בטוח באיך לשלוף נתונים בsp.
 

עידו פ

New member
לגבי SQL שמופיע בכמה מקומות

משפט כמו "אני אצטרך את אותו SQL בכמה מקומות" מוביל למסקנה ראשונית של עיצוב לקוי. מאחר ומידע שייך לישות מסוימת, אין סיבה שישויות שונות יפנו לאותו מידע, משמע - אין סיבה שמשפט SQL שפונה לטבלה/VIEW כלשהו יימצא ביותר ממקום אחד במערכת (ביותר מישות אחת). אין מניעה שבאותה מחלקה תהיה פניה למידע בצורות שונות (פעם להביא שדה X ופעם להביא את שדה Y, פעם להביא רשומות לפי א'-ב'-ג' ופעם בשביל להביא רשומות לפי ד'-ה'-ו') אבל ההגיון אומר שישות אחראית על הנתונים שלה ואף ישות אחרת לא אמורה "להכנס לה לנשמה".
 

duducohn

New member
סתם הרהורים

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

עידו פ

New member
בשנים האחרונת בהם עסקתי

בניתוח OO (או בקיצור OOA), שמתי לב לדברים הבאים: 1. התוצרים של האפיון היו יותר נוחים להצגה ללקוח מאשר תוצרי האפיון שהיו במתודולוגיות הישנות (DFD וכדומה) 2. התוצרים של האפיון היו נוחים לתרגום ע"י המעצב לארכיטקטורה של OO (או גם במקרה שלי לארכיט' SOA). כבר שמעתי ממעצבים שונים שעבדו בארכיטקטורות הנ"ל ושקיבלו תוצרי אפיון בשיטות ישנות על הסיבוכים שהיו להם בלתרגם שיטה אחת לאחרת
 

ייוניי

New member
לדעתי

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