Java - Encapsulation

Java - Encapsulation

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

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


/* File name : EncapTest.java */
public class EncapTest{

private String name;
private String idNum;
private int age;

public int getAge(){
return age;
}

public String getName(){
return name;
}

public String getIdNum(){
return idNum;
}

public void setAge( int newAge){
age = newAge;
}

public void setName(String newName){
name = newName;
}

public void setIdNum( String newId){
idNum = newId;
}
}
 

nocgod

New member
כימוס

כימוס הוא אחד העקרונות המנחים את התכנות מונחה עצמים. הוא לא קשור לשפת תוכנה אלא לרעיון עבודה.
הרעיון אומר שלמשתמש לא אמורה להיות גישה חופשית לדברים שלא קשורים אליו, במיוחד לא לדברים הקשורים ישירות ללוגיקת העבודה של המחלקה.
הדוגמא שהבאת מצויינת: למשתמש במחלקה אין גישה ישירה לשדות, אלא רק באמצעות getter/setter.
שים לב שזה לא בהכרח נכון לתת גישה לשינוי התעודת זהות לצורך העניין מאחר והיא מלווה אותנו עד סוף החיים, למרות זאת נתת גישה כזאת, מה שפוגע קצת בעיקרון ההכמסה, אבל לא בצורה פאטאלית.
דבר המבטל את ההכמסה הוא מערכת היחסים בין המחלקות: 2 אובייקטים מאותה מחלקה יכולים לגשת לשדות הפרטיים אחד של השני, מאחר וprivate קובע פרטיות ביחס למחלקה לא ביחס לאובייקט (ככה זה ב ++C ואני חושב שככה זה גם ב java)

עוד קצת מידע על ההכמסה אפשר למצוא בויקיפדיה כמובן ובספרות מקצועית.
http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

חג שמייח
 
תודה על התשובה המהירה,

למחלקה שאני נתתי דוגמא אמנם אי אפשר לגשת ישירות לשדות אבל אפשר לקרוא ולשנות אותם ולהשתמש בהם.

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

תודה!
 

nocgod

New member
בדוגמא שלך...

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

רעיון הgetters setters הוא לנהל את זרימת המידע, לא תמיד למשתמש צריכה להיות האפשרות לשנות מידע באובייקט בצורה לא מבוקרת ולא תמיד למשתמש צריכה להיות האפשרות להוציא מאובייקט מידע כזה או אחר.
במקרים טריוויאלים כמו זה שהבאת קשה לראות את החשיבות של העיקרון הזה, אבל בעתיד תלמד ג'אווה ותראה שללא כימוס דברים כמו כרטיס האשראי שלך היו יכולים להיות ידועים לכל, כי כל מה שהייתי צריך לעשות זה
credit.number והייתי מקבל או מכניס מידע לשדה נורא רגיש שהגישה אליו צריכה להיות מאוד מוגבלת

רעיון הכימוס גם שימושי ביצירת משתנים immutable, דוגמא לכאלה הם String, Integer, Double וכו'... אלו הם מחלקות המכילות מידע אותו לא ניתן לשנות (בג'אווה וב#C לפחות) אך אפשר להשיג את המידע הזה אחרי שאיתחלת אובייקט מטיפוס המחלקה.

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

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

BravoMan

Active member
אולי עוד כמה דוגמאות יעזרו להמחיש את העניין:

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

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

הרי דלי תמיד נשאר באותו גודל.

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

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

נניח להכניס 10 ליטר לדלי של 5 ליטר.

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

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

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

יש לו עורך קבוע, וספרת ביקורת.
אגב, אופן חישוב הספרה הזו כלל אינו סודי, אלא אלגוריתם סטנדרטי.
http://en.wikipedia.org/wiki/Luhn_algorithm

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

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

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

אם הכנסת השם מתבצעת ישירות למשתנה name אז כמו ש-nocgod הסביר לך בתגובה מעלי, תצטרך לעבור על כל מקום בקוד שבו מכניסים שם לאוביקט כלשהו ולשנות אותו.

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

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

בעולם האמתי, לרוב זה לא המצב.

ודוגמה אחרונה:

נגיד שברצונך לממש "מחסנית" - First in First Out.
המחסנית מאפשרת לך לעשות PUSH ו-POP כדי להכניס ולהוציא נתונים.

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

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

ללא כימוס, אין דרך לוודא את שלמות המחסנית.
 

BravoMan

Active member
שני תיקונים קטנים להודעה שלי:

Exception בעברית היא "חריגה", לא "חריג" (לפחות לפי הקורסים שאני למדתי, תרגומי מונחים לפעמים משתנים).

ומחסנית היא לא FIFO אלא LIFO כלומר Last In First Out
 
למעלה