שאלה בעיצוב DESIGN של מודול דחיסה

GilBates

New member
שאלה בעיצוב DESIGN של מודול דחיסה

נניח שיש 4 פרמטרים קבועים לכל סוג של דחיסה (אחד מהם זה סוג הדחיסה) יש לי מודול דחיסה שכתבתי, שמקבל בנוסף עוד מספר פרמטרים נוספים. מבנה המחלקות הרלוונטי הוא : מחלקת בסיס GeneralParams (שמכילה את 4 הפרמטרים הקבועים) ומחלקה יורשת MyParams שמוסיפה את הפרמטרים שלי. אני רוצה לשלוח לפעמים לדחיסה סטנדרטית ולפעמים לשלי. יש לי פונקציה כללית Compress(GeneralParams) שאני רוצה לשלוח אליה מבנה מסוג GeneralParams והיא תבדוק את השדה הרלוונטי (סוג הדחיסה)ותקרא לדחיסה שלי MyCompress(params) או לדחיסות אחרות לפיו. אבל - MyCompress מקבלת כרגע מבנה של הבן MyParams כי בתוכה היא מתייחסת לשדות הנוספים כמובן. ויש ב-MyParams כמובן גם פונקציות נוספות (מלד ה-Getterים של הפרמטרים הנוספים) איך פותרים את זה בצורה הטובה ביותר? מקווה שהצלחתי להבהיר את עצמי. איך אני עושה את זה נכון
 

ייוניי

New member
העיצוב שלך קצת בעייתי

אבל אני יכול להציע לך שתי שיטות להתמודד עם זה: 1. unboxing - כלומר אם החלטת להפעיל את MyCompress לנסות להמיר את הפרמטר מ GeneralParams ל MyParams ולקוות לטוב... 2. להוסיף פרמטר של MyParams ל Compress ולהשתמש או לא להשתמש בו בהתאם. (ולקוות שהוא לא null כי הוא יכול להיות) לידיעתך מה שאתה לא עושה נכון הם שני דברים - אתה משתמש במחלקות אבל לא מסתיר את המידע שבהן בפנים ואתה משתמש בהורשה על מנת להוסיף לממשק החיצוני של מחלקה במקום להשתמש בכך על מנת לשנות את המימוש של הממשק שלה.
 

GilBates

New member
יוני, תודה רבה על התגובה - איך אתה

מציע לעצב באופן יותר נכון?
 

ייוניי

New member
שאלות מקדימות

קודם כל נשאלת השאלה למה אם כבר בחרתי להשתמש ב MyParams זה לא ברור שאני מעוניין ב MyCompress ואם כן אז למה אני צריך להעביר את זה דרך Compress בכלל? עכשיו יש גם את עניין אופי הפרמטרים, בגישת OOP לא נהוג להעביר "מבני נתונים" בין מחלקות אלא לשמור את המבנה מוסתר ולחשוף מתודות לביצוע פעולות. בגישה הפרוצדוראלית נהוג להעביר מבני נתונים והפתרון למצב שלך הוא פשוט לעבוד עם מבנה נתונים אחד (קרי MyParams) ואם פונקציות מסויימות זקוקות רק לחלק מהנתונים אין שום בעיה הן לא צריכות לשלוף את השאר. אם מבנה הנתונים מתחיל לגדול מדי נהוג פשוט להעתיק את הנתונים למבנה קטן יותר בהתאם לצורך. אבל אם אתה בכוון של OOP אני מציע שתבחן את הפרמטרים הללו והאם ניתן להסתיר אותם בתוך המחלקה וליישם את ההתנהגות שהם מכתיבים בתוכה. בסופו של דבר אתה אמור להגיע למצב שבו מתודת Compress תשב בכלל במחלקות GeneralParams ו MyParams רק תיושם באופן שונה (או משהו בסגנון). אם תציג קצת יותר את אופי הפרמטרים אוכל לעזור יותר.
 

ddalus

New member
המממ

לא יודע אם זה ממש נכון לומר שלא נהוג להעביר מבני נתונים, לפחות by reference - בכל זאת מדובר במשהו שאפילו כמה DP בסיסיים מתבססים עליו. מה שאכן לא מקובל הוא להעביר "מבני נתונים רזים" - כלומר אובייקטים שכל מה שיש בהם הוא כמה משתנים בלי פונקציונליות פנימית, ואפילו זה לא ממש חוק חקוק בסלע (ראה JavaBeans ו-properties ב-VB ודוט נט). אבל לפי מה שהבנתי "מבנה נתונים רזה" זה בדיוק מה שמועבר, ולכן ההערות שלך במקום. רק רציתי להעיר שני דברים: 1. הפסקה הראשונה של ייוניי מאוד נכונה. לפעמים הבעיה היא "אינוס יתר" של חשיבה מונחית עצמים, כאשר הפתרון הוא מאוד פשוט מבחינה לוגית. 2. למיטב הבנתי, התיאור של העיצוב המקורי שובר את ה-Liskov Substitution Principle, הלא כן? צק צק צק. 3. לבעייה המתוארת הספציפית של שימוש בכמה מחלקות (שלא ניתנות לשינוי) ושדורשות פרמטרים שונים, הייתי משתמש ב-Adapter, אבל מבחינת עיצוב יותר נבון... 4. סתם כמה Design Patterns שעולים לי בראש, בלי שום הבטחה לרלוונטיות שיש או אין להם: Factory, Strategy, Template Method
 

ייוניי

New member
חקוק, חקוק

גם JavaBeans וגם Properties שוברים encapsulation ו data abstraction ולא תמצא אף DP שעושה שימוש באובייקטים שחושפים מידע פרטי... זה שהתופעה קיימת לא אומר שהיא נכונה. להעביר אובייקטים זה דבר מומלץ ואף חובה אבל באובייקטים המידע מוחבא ואיננו נגיש ואילו במבני נתונים (כמו ב struct ב ++C) המידע חשוף ועובר בין פונקציות במערכת. ברגע שמתחילים לערבב בין שני הדברים לעניינים יש נטיה להסתבך.
 

GilBates

New member
תשובות - אני מבצע parsing לשורת

הפקודה (command line) ע"י פונקציה parseParams שמחזירה מבנה מטיפוס האב GeneralParams אם נבחרה שיטת הדחיסה שלי גם אין בעיה כי מוקצה אובייקט מטיפוס הבן (שיכול להיות מיוצג ע"ע רפרנס מטיפוס האב). בתוך המבנה יש גם את שיטת הדחיסה (שלי והאחרות) ואז אני שואל עליה בתוך compress הראשית ומשם שולח לMyCompress או לפונקציות האחרות בהתאם לשדה זה. לשם הפישוט: הפרמטרים הכללים GeneralParams הם: 1. קובץ מקור 2. קובץ יעד 3. שיטת דחיסה (ZIP , GZIP, שיטת הדחיסה שלי (TRN)) 4. compression level אני מוסיף עוד פרמטרים : 5. גודל בלוק לדחיסה ועוד מספר פרמטרים משלי. איך אני יכול להסתיר את ההתנהגות שלהם? זה פרמטרים שמשמשים אח"כ בתהליך הדחיסה שלי אולי כל הדיזיין שלי פגום...
 

ייוניי

New member
הכי פשוט

אני מניח שאתה כותב ב JAVA מהפרופיל שלך... קודם כל במקום לפרסר את ה command line לתוך אובייקט הייתי בונה אובייקט שמקבל מחרוזת וחושף מתודות לגישה לפרמטרים. הממשק שלו יהיה משהו כזה:
public interface CommandLineParser { string getParam(int index); string getSwitchParam(string switch); }​
מתודה אחת מביאה פרמטר רגיל לפי המספר שלו והשניה מביאה switch-ים למיניהם... עכשיו נשארו לי רק שלוש מחלקות מעניינות לכתוב:
public interface Compressor { void compress(); } public class CompressorSelector { CommandLineParser _paramsParser; public CompressorSelector(CommandLineParser paramsParser) { _paramsParser = paramsParser; } public Compressor getCompressor() { string compressionType = _paramsParser.getParam(2); // return the right compressor (two options below); } public void selectAndCompress() { getCompressor().compress(); } } public class MyCompressor implements Compressor { CommandLineParser _paramsParser; public MyCompressor(CommandLineParser paramsParser) { _paramsParser = paramsParser; } public void compress() { // compress... } } public class OtherCompressors implements Compressor { CommandLineParser _paramsParser; public MyCompressor(CommandLineParser paramsParser) { _paramsParser = paramsParser; } public void compress() { // compress... } }​
אני נותן לקומפרסורים גישה מלאה ל command line כי מדובר במחלקות מרכזיות שה command line הוא הממשק שלהם למשתמש וכך כל תוספת/שינוי ב command line לא דורשת התאמות מיוחדות מעבר לקוד הרלוונטי. אפשר לוותר על הפרמטרים הממוספרים ולקרוא להם בשמות אם אתה חושש ששינוי בפרמטרים שאינם switch-ים ידרוש שינוי בשתי מחלקות ה compressor... בכל אופן שים לב שהבחירה בין MyCompressor ל OtherCompressors נמצאת במקום אחד בלבד במערכת ולא בשני מקומות כמו אצלך (בהקצאת אובייקט ה Params ובפונקציית compress...). נ.ב. אם טעיתי ב syntax של java אני מתנצל פשוט לקחתי #C ושיניתי את ה ':' ל implements...
 

GilBates

New member
קודם כל תודה רבה, אני אבדוק את זה

כבר בשבוע הבא. שוב תודה
 

CoolerMaster

New member
זה בעצם Factory Method

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