Thursday, March 26, 2009

Thinking in Objects

Long ago, I had to write some code which do this:

Given two list 1. oldList 2. newList you have to find out:
1. The elements which are common in both list.
2. The elements which are new in the newList (means they were added)
3. The elements which are in oldList and not in newList (means they were deleted)

Definitely not the hardest kind of problem! I quickly grabbed my keyboard and wrote something like this :

public class CollectionModificationHelper {

public static List getCommonElements(List oldCollection, List newCollection) {
//iterate on oldCollection and find the common elements
}

public static List getNewElements(List oldCollection, List newCollection) {
//iterate on newCollection and find the new elements
}

public static List getRemovedElements(List oldCollection, List newCollection) {
//iterate on oldCollection and find the deleted elements
}
}



Simple solution and works fine. Only problem is, its iterates over the same collection again and again to find the old/common/new elements, not a big deal if you are not worried about performance.

Yesterday, I had to write the same code! Almost 4 years after I wrote the first version, seeing a hell lot of code, reading a hell lot of materials about Object Oriented Programming and design, this is what I wrote without much thinking:


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import org.apache.commons.collections.CollectionUtils;

public class CollectionModificationHelper<T> {
private Collection<T> oldCollection;
private Collection<T> newCollection;

private Collection<T> newElements = new ArrayList<T>();
private Collection<T> commonElements = new ArrayList<T>();
private Collection<T> removedElements = new ArrayList<T>();

public CollectionModificationHelper(Collection<T> oldCollection,
Collection
<T> newCollection) {
super();
this.oldCollection = oldCollection;
this.newCollection = newCollection;

init();
}

public CollectionModificationHelper(T[] oldArray, T[] newArray) {
this((oldArray == null ? null : Arrays.asList(oldArray)), (newArray == null ? null : Arrays.asList(newArray)));
}

private void init() {
if(CollectionUtils.isEmpty(oldCollection) && CollectionUtils.isEmpty(newCollection)) {
//nothing to do!
} else if(CollectionUtils.isEmpty(oldCollection)) {
if(!CollectionUtils.isEmpty(newCollection)) {
this.newElements = new ArrayList<T>(newCollection);
}
}
else if(CollectionUtils.isEmpty(newCollection)) {
if(!CollectionUtils.isEmpty(oldCollection)) {
this.removedElements = new ArrayList<T>(oldCollection);
}
}
else {
for(T item : oldCollection) {
if(newCollection.contains(item)) {
commonElements.add(item);
}
else {
removedElements.add(item);
}
}

newElements.addAll(newCollection);
//first add all new Collection in new element.
newElements.removeAll(commonElements); //then remove the common elements.
}
}

public Collection<T> getCommonElements() {
return commonElements;
}

public Collection<T> getNewElements() {
return newElements;
}

public Collection<T> getRemovedElements() {
return removedElements;
}
}



Its interesting to see how your thought process changes over time. As far as I my opinion goes, the 2nd solution is much elegant than the first one! In the first one, I was just not thinking in Objects!

Over the years, as you are involved in serious development, you will surely learn many technologies, tricks, techniques. But I think you must thrive to learn how to think in Objects. Once you can achieve that, you will find a whole new world of idea about how the objects/components should interact. Once you are there, you will love it. I am not saying I am there already, but I sure did come a long way from where I started and trying to master it everyday. How do you prepare yourself? Practice & Read. My two best guru in this area are both named "Martin"! "Robert C. Martin" & "Martin Fowler" :) Do have a look at their work if you haven't already.

Lastly, what code is good code without some unit tests!


import java.util.Collection;

import junit.framework.TestCase;

public class CollectionModificationHelperTest extends TestCase {
public void testWithNullCollections() {
Integer[] old = null;
Integer[] newC = null;

CollectionModificationHelper<Integer> collectionModificationHelper = new CollectionModificationHelper<Integer>( old, newC);

assertEquals(0, collectionModificationHelper.getCommonElements().size());
assertEquals(0, collectionModificationHelper.getNewElements().size());
assertEquals(0, collectionModificationHelper.getRemovedElements().size());

old = new Integer[]{1,2,3,4};
newC = null;

collectionModificationHelper = new CollectionModificationHelper<Integer>( old, newC);
assertEquals(0, collectionModificationHelper.getCommonElements().size());
assertEquals(0, collectionModificationHelper.getNewElements().size());
assertEquals(old.length, collectionModificationHelper.getRemovedElements().size());

old = null;
newC = new Integer[]{1,2,3,4};

collectionModificationHelper = new CollectionModificationHelper<Integer>( old, newC);
assertEquals(0, collectionModificationHelper.getCommonElements().size());
assertEquals(newC.length, collectionModificationHelper.getNewElements().size());
assertEquals(0, collectionModificationHelper.getRemovedElements().size());
}

public void testCollectionModificationHelper() {
Integer[] old = new Integer[]{1,2,3,4,5,6,7};
Integer[] newC = new Integer[]{1,4,10,11,12,13};

CollectionModificationHelper<Integer> collectionModificationHelper = new CollectionModificationHelper<Integer>( old, newC);

Collection<Integer> commonElements = collectionModificationHelper.getCommonElements();
Collection<Integer> newElements = collectionModificationHelper.getNewElements();
Collection<Integer> removedElements = collectionModificationHelper.getRemovedElements();

System.out.println("commonElements: " commonElements);
System.out.println("newElements: " newElements);
System.out.println("removedElements: " removedElements);

assertEquals(2, commonElements.size());
assertEquals(4, newElements.size());
assertEquals(5, removedElements.size());
}
}