The Artima Developer Community
Sponsored Link

Agile Buzz Forum
Immutable Objects

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Keith Ray

Posts: 658
Nickname: keithray
Registered: May, 2003

Keith Ray is multi-platform software developer and Team Leader
Immutable Objects Posted: Jan 20, 2004 11:45 AM
Reply to this message Reply

This post originated from an RSS feed registered with Agile Buzz by Keith Ray.
Original Post: Immutable Objects
Feed Title: MemoRanda
Feed URL: http://homepage.mac.com/1/homepage404ErrorPage.html
Feed Description: Keith Ray's notes to be remembered on agile software development, project management, oo programming, and other topics.
Latest Agile Buzz Posts
Latest Agile Buzz Posts by Keith Ray
Latest Posts From MemoRanda

Advertisement

Immutable Objects are simply objects that can't be changed. Because they can't be changed, you never have to spend time debugging unintentional changes. Mutable objects, if access is not carefully controlled, can be as bad as global variables when it comes to figuring out how an object got changed.

In Eric Evan's book, Domain-Driven Design, he divides domain objects into Entities and Values, where Values are immutable objects. One of his examples is a Person as an Entity, and the person's Address or ContactInfo as a Value.

I will demonstrate... To keep the example short, I will leave out all of contact info's data except for phone number, and not worry about the format of the phone number. (Nor will I worry about persistence, databases, etc.) We'll have a Person Entity object and a ContactInfo Value object.

class ContactInfo
{
    public ContactInfo( String phoneIn )
    {
        phone = phoneIn;
    }

    public void PrintOut()
    {
        System.out.println( "ContactInfo = {" );
        System.out.println( "  phone = " + phone );
        System.out.println( "}" );
    }

    public boolean matchesPhone( String phoneIn )
    {
        return ( phone.equals( phoneIn ) );
    }
    private String phone;
}
 
class Person
{
    public Person( String nameIn )
    {
        name = nameIn;
    }

    void setContactInfo( ContactInfo ciIn )
    {
        myContactInfo = ciIn;
    }

    public boolean matchesPhone( String phoneIn )
    {
        return myContactInfo.matchesPhone( phoneIn );
    }

    public void PrintOut()
    {
        System.out.println( "Person = {" );
        System.out.println( "  name = " + name );
        System.out.print( "myContactInfo = " );
        myContactInfo.PrintOut();
        System.out.println( "}" );
    }

    private String name;
    private ContactInfo myContactInfo;
};


public class ImmutableObjectDemo 
{
    public static void main (String args[]) 
    {
        Person aPerson = new Person( "Stan" );
        ContactInfo stanInfo = new ContactInfo( "555-STAN" );
        aPerson.setContactInfo( stanInfo );

        aPerson.PrintOut();

        System.out.println( "aPerson matchesPhone 555-STAN = " 
            + aPerson.matchesPhone( "555-STAN" ) );

        ContactInfo newStanInfo = new ContactInfo( "555-1234" );
        aPerson.setContactInfo( newStanInfo );

        aPerson.PrintOut();

        System.out.println( "aPerson matchesPhone 555-STAN = " 
            + aPerson.matchesPhone( "555-STAN" ) );

        System.out.println( "aPerson matchesPhone 555-1234 = " 
            + aPerson.matchesPhone( "555-1234" ) );
    }
}

Running the above provides this output: (indentation cleaned up)

Person = {
    name = Stan
    myContactInfo = ContactInfo = {
        phone = 555-STAN
    }
}
aPerson matchesPhone 555-STAN = true
Person = {
    name = Stan
    myContactInfo = ContactInfo = {
        phone = 555-1234
    }
}
aPerson matchesPhone 555-STAN = false
aPerson matchesPhone 555-1234 = true

ContactInfo is immutable simply by keeping its data members private, and not providing any member functions that modify its data. If I had implemented an accessor method to return the phone number, that would be safe, because the I used a String to hold the phone number, which is also immutable. Person is not immutable because "setContactInfo" changes its member data.

Now let's say we need to provide an "update phone number" method on Person. This is one implementation:

 
class Person
{
....
    public void updatePhoneNumber( String newNumber )
    {
        myContactInfo = new ContactInfo( newNumber );
    }
...
}

However, that can get rather wordy (imagine all the other data that I'm leaving out:

 
class Person
{
...
    public void updatePhoneNumber( String newNumber )
    {
        myContactInfo = new ContactInfo( newNumber,
            myContactInfo.getStreetAddress(),
            myContactInfo.getCity(),
            myContactInfo.getState(), 
            myContactInfo.getZip() );
    }
    public void updateCity( String newCity )
    {
        myContactInfo = new ContactInfo( 
            myContactInfo.getPhoneNumber(),
            myContactInfo.getStreetAddress(),
            newCity,
            myContactInfo.getState(), 
            myContactInfo.getZip() );
    }
...
}

So it would be better for ContactInfo to provide us with a new copy of itself, with the updated data. This will provide better cohesion as well -- if we added a new field (like 'company name') only methods in ContactInfo would have to change; the existing methods in Person will not have to change.


class Person
{
...
    public void updatePhoneNumber( String newNumber )
    {
        myContactInfo = myContactInfo.newPhoneNumber( newNumber );
    }
...
}

class ContactInfo
{
...
    ContactInfo newPhoneNumber( String phoneIn )
    {
        return new ContactInfo( phoneIn /* and address, city, 
                    state, zip, and other members... */ );
    }
...
}

Given that ContactInfo is immutable, we have an opportunity to maintain an list of previous ContactInfo objects that we would not have had if we had just been modifying the data of a ContactInfo object in-place. Suppose we talk to our customer about this, and he says that yes, we want to be able to find a Person given an old phone number. So we can make the following changes (see lines with /*new*/ added:


import java.util.*;

class ContactInfo
{
    public ContactInfo( String phoneIn )
    {
        phone = phoneIn;
    }

    public void PrintOut()
    {
        System.out.println( "ContactInfo = {" );
        System.out.println( "  phone = " + phone );
        System.out.println( "}" );
    }

    public boolean matchesPhone( String phoneIn )
    {
        return ( phone.equals( phoneIn ) );
    }
    
    ContactInfo newPhoneNumber( String phoneIn )
    {
        return new ContactInfo( phoneIn );
    }
    private String phone;
}

class Person
{
    public Person( String nameIn )
    {
        name = nameIn;
        oldContactInfo = new Stack();
    }

    void setContactInfo( ContactInfo ciIn )
    {
        myContactInfo = ciIn;
    }

    public boolean matchesPhone( String phoneIn )
    {
        if ( myContactInfo.matchesPhone( phoneIn ) )         /*new*/
        {
            return true;                                     /*new*/
        }
        
        ListIterator i = oldContactInfo.listIterator();      /*new*/
        while ( i.hasNext() )                                /*new*/
        {
            ContactInfo oldContact = (ContactInfo) i.next(); /*new*/
                
            if ( oldContact.matchesPhone( phoneIn ) )        /*new*/
            {
                return true;                                 /*new*/
            }
        }
        return false;                                        /*new*/
    }

    public void PrintOut()
    {
        System.out.println( "Person = {" );
        System.out.println( "  name = " + name );
        System.out.print( "myContactInfo = " );
        myContactInfo.PrintOut();
        System.out.println( "  oldContactInfo = {" );
        ListIterator i = oldContactInfo.listIterator();      /*new*/
        while ( i.hasNext() )                                /*new*/
        {
            ContactInfo oldContact = (ContactInfo) i.next(); /*new*/
            oldContact.PrintOut();                           /*new*/
        }
        System.out.println( "}" );                           /*new*/
        System.out.println( "}" );
    }
    
    public void updatePhoneNumber( String newNumber )
    {
        oldContactInfo.push( myContactInfo );               /*new*/
        myContactInfo = myContactInfo.newPhoneNumber( newNumber );
    }

    private String name;
    private ContactInfo myContactInfo;
    private Stack oldContactInfo;                           /*new*/
};

public class ImmutableObjectDemo 
{
    public static void main (String args[]) 
    {
        Person aPerson = new Person( "Stan" );
        ContactInfo stanInfo = new ContactInfo( "555-STAN" );
        aPerson.setContactInfo( stanInfo );

        aPerson.PrintOut();

        System.out.println( "aPerson matchesPhone 555-STAN = " 
            + aPerson.matchesPhone( "555-STAN" ) );

        aPerson.updatePhoneNumber( "555-1234" );

        aPerson.PrintOut();

        System.out.println( "aPerson matchesPhone 555-STAN = " 
            + aPerson.matchesPhone( "555-STAN" ) );

        System.out.println( "aPerson matchesPhone 555-1234 = " 
            + aPerson.matchesPhone( "555-1234" ) );
    }
}

With this output (indentation cleaned up):


Person = {
    name = Stan
    myContactInfo = ContactInfo = {
        phone = 555-STAN
    }
    oldContactInfo = {
    }
}
aPerson matchesPhone 555-STAN = true
Person = {
    name = Stan
    myContactInfo = ContactInfo = {
        phone = 555-1234
    }
    oldContactInfo = {
        ContactInfo = {
            phone = 555-STAN
        }
    }
}
aPerson matchesPhone 555-STAN = true
aPerson matchesPhone 555-1234 = true

Now, we may actually want our intentions to be more clearly revealed after this last change. For example, we could have two methods named "matchesCurrentPhone" and "matchesPreviousPhones" to break down that slightly-too-long "matchesPhone" function. We might even rename the "matchesPhone" function to something like "matchesCurrentAndPreviousPhones".

Immutable objects imply Side-Effect-Free Functions, which Eric Evans says is one of the idioms that contribute to "Supple Design". The other idioms listed in his book are Assertions, Intention-Revealing Interfaces, Standalone Classes, and Conceptual Contours. Check it out.

Read: Immutable Objects

Topic: Matching under X11 Previous Topic   Next Topic Topic: Not paying attention

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use