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.
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:
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.