יום שלישי, 4 בדצמבר 2018

Immutable Class in Java

Immutable Class


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

בדוגמה הבאה לא ניתן לשנות את התוכן של s1 בשום אופן:
String s1 = "ABC";


השורה הבאה לא משנה את התוכן שהיה באובייקט עליו הצביע s1, אלא יוצרת אובייקט חדש ואומרת ל-s1 להצביע כעת עליו:
s1 = s1 + "D";


יהיה אשר יהיה הקוד בפונקציה ()f, ההדפסה בלי ספק תהיה "abcde":
String s2 = "abcde";
f(s2);
System.out.println(s2);




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








כללי אצבע ביצירת Immutable Object משלנו:

1. על כל השדות להיות private ואין לספק פונקציות Set.
2. כאשר פונקציה מחזירה member של המחלקה, (מדובר בעיקר בפונקציות Get), אם ה-member הזה הוא
    לא Immutable בעצמו יש להחזיר Clone שלו.
3. כל פונקציה שעושה שינוי כלשהו על המחלקה לא תשנה את האובייקט עליו היא מופעלת אלא תחזיר אובייקט חדש
    אשר מכיל את השינוי.
4. אם המחלקה מכילה אובייקטים שאינם Immutable בעצמם יש לדאוג ב-constructor לשמור clone שלהם.
    אם לא היינו עושים זאת, במידה והשולח עדיין היה מחזיק בכתובת שלהם הוא היה יכול לשנות אותם אחרי יצירת האובייקט.
5. מומלץ למנוע ירושה של המחלקה (על ידי המילה final בהצהרת המחלקה). אחרת יורשים יוכלו לדרוס את
    הפונקציות ולבטל את תכונת ה-Immutable.
6. מומלץ שכל ה-members של המחלקה יהיו final, כך מובטח שהם מאותחלים רק ב-constructors.





דוגמת קוד:

public class MutablePoint {
    private int x;
    private int y;

    public MutablePoint(int x, int y) {
        this.x = x;
        this.y = y;
    }
    
    public MutablePoint() {
        this(0, 0);
    }
    
    public MutablePoint(MutablePoint otherPoint) {
        this(otherPoint.getX(), otherPoint.getY());
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }
    
    /**
     * Add point to this point
     */
    public void addPoint(MutablePoint pointToAdd) {
        this.x += pointToAdd.x;
        this.y += pointToAdd.y;
    }

    @Override
    protected MutablePoint clone() {
        return new MutablePoint(this);
    }
}

public final class MyImmutableClass {
    private final int primitiveInt;
    private final boolean primitiveBoolean;
    private final String immutableString;
    private final MutablePoint mutablePoint;

    public MyImmutableClass(int primitiveInt,
                            boolean primitiveBoolean,
                            String immutableString,
                            MutablePoint mutablePoint) {
        
        this.primitiveInt = primitiveInt;
        this.primitiveBoolean = primitiveBoolean;
        this.immutableString = immutableString;
        this.mutablePoint = mutablePoint.clone();
    }
    
    public MyImmutableClass(MyImmutableClass immutableClassToCopy) {
        this(immutableClassToCopy.getPrimitiveInt(),
             immutableClassToCopy.getPrimitiveBoolean(),
             immutableClassToCopy.getImmutableString(),
             immutableClassToCopy.getMutablePoint());
    }

    public int getPrimitiveInt() {
        return primitiveInt;
    }

    public boolean getPrimitiveBoolean() {
        return primitiveBoolean;
    }

    public String getImmutableString() {
        return immutableString;
    }

    public MutablePoint getMutablePoint() {
        return mutablePoint.clone();
    }
    
    public MyImmutableClass addPoint(MutablePoint pointToAdd) {
        MyImmutableClass myImmutableClassCopy = this.clone();
        myImmutableClassCopy.mutablePoint.addPoint(pointToAdd);
        return myImmutableClassCopy;
    }
    
    @Override
    protected MyImmutableClass clone() {
        return new MyImmutableClass(this);
    }
}









........................

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

רק תיארנו בכלליות את הנושא, מומלץ להרחיב ולקרוא את המאמר המוצלח ב-DZone.


אין תגובות:

הוסף רשומת תגובה