Showing posts with label factory. Show all posts
Showing posts with label factory. Show all posts

Tuesday, March 31, 2009

Examples of Lazy Lists and Factories

In this post, I'm going to examine Lazy Lists and the Apache Factory.

In accordance with most of the Apache Commons Collection utilities, a LazyList is simply a decorator (and at this point that shouldn't be a surprise). It decorates another List creating a sort-of "list on demand" list. Calling the method get(int index) when the index is greater than the size of the list will grow the list with objects from the given factory. Using the get(int index) method to get an object that the factory did not fill in will result in an IndexOutOfBoundsException.

For all of the examples, lets use the following simple factory.
The Code:

package com.blogspot.apachecommonstipsandtricks.lazymapexamples;
import java.util.*;
import org.apache.commons.collections.*;
public class LazyListFactory implements Factory
{
private static int i = 0;
private List<String> strings = new ArrayList();
public LazyListFactory(List<String> strings)
{
this.strings.addAll(strings);
}
public Object create()
{
return this.strings.get(i++);
}
}

In this example Java code we have implemented the Factory interface, which defines a Object create(), but more importantly the constructor takes as an argument a List of Strings. These strings are effectively the "feed" that the create method will use. A more practical solution would be some form of a data source, but that would result in a lot of code, and this is simple. So with this factory, let us create some lazy data!

Our first java example of a lazy list.
The Code:

package com.blogspot.apachecommonstipsandtricks.lazymapexamples;
import java.util.*;
import org.apache.commons.collections.list.*;
public class ListManager
{
public static void main(String[] args)
{
// First lets create a simple list "A" through "C"
List<String> backingList = new ArrayList<String>( Arrays.asList("A","B","C") );
// Now we create a Decorated List with homegrown class called LazyListFactory which implements Factory
List<String> list = LazyList.decorate( GrowthList.decorate( backingList ), new LazyListFactory( Arrays.asList("D","E","F","G") ) );
System.out.println("The size method on list thinks there are " + list.size() + " elements in the array list. but Im going to get 7!");
// as the more and more elements are requested, the factory creates more..
for (int i = 0; i < 7; i++)
{
Object o = list.get(i);
System.out.println( " Element [" + i + "] = " + o );
}
}
}


Results in

The size method on list thinks there are 3 elements in the array list. but Im going to get 7!
Element [0] = A
Element [1] = B
Element [2] = C
Element [3] = D
Element [4] = E
Element [5] = F
Element [6] = G


In this example, we first created a list, with Strings "A","B", and "C" than we decorated it, and attached a factory constructed with four more additional strings "D" through "G". I knew there was seven items in the list, so I forced it to loop to seven. Did you notice, the list thought the size was only 3. You might be thinking, which I am too, that’s not very helpful. How can I tell how much to loop through? Let’s look at this same example, but with some minor modifications.

The Code:

package com.blogspot.apachecommonstipsandtricks.lazymapexamples;
import java.util.*;
import org.apache.commons.collections.list.*;
public class ListManager
{
public static void main(String[] args)
{
// First lets create a simple list "A" through "C"
List<String> backingList = new ArrayList<String>( Arrays.asList("A","B","C") );
// Now we create a Decorated List with homegrown class called LazyListFactory which implements Factory
List<String> list = LazyList.decorate( GrowthList.decorate( backingList ), new LazyListFactory( Arrays.asList("D","E","F","G") ) );

// Now puit "Z" in position 15
list.set( 15, "Z" );

System.out.println("After putting \"Z\" in position 15, the size is equal to " + list.size());

System.out.println("Which allows us to us a standard for-loop with a IndexOutOfBoundsException try-catch block.");
for (int i = 0; i < list.size(); i++)
{
try
{
Object o = list.get(i);
System.out.println( " Element [" + i + "] = " + o );
} catch (IndexOutOfBoundsException e) {}
}

}
}


You might have immediately noticed I put another String, "Z" at position 15, and now, the size is 15. I find it likely that this might be a solution to the "unknown size problem". The problem statement might go something like this, create a LazyList and set the last element to a known end state. Therefore, the user could pick what they wanted to consume out of the list while knowing the end is near at 15.

The results of the program above would look like this.

After putting "Z" in position 15, the size is equal to 16
Which allows us to us a standard for-loop with a IndexOutOfBoundsException try-catch block.
Element [0] = A
Element [1] = B
Element [2] = C
Element [3] = D
Element [4] = E
Element [5] = F
Element [6] = G
Element [15] = Z

If you are hung up on swallowing the IndexOutOfBoundsException exception or simply don’t like the idea of having to put in the last element this next solution may suite you. It uses the Java 1.5 for-loop.

The Code:

package com.blogspot.apachecommonstipsandtricks.lazymapexamples;
import java.util.*;
import org.apache.commons.collections.list.*;
public class ListManager
{
public static void main(String[] args)
{
// First lets create a simple list "A" through "C"
List<String> backingList = new ArrayList<String>( Arrays.asList("A","B","C") );
// Now we create a Decorated List with homegrown class called LazyListFactory which implements Factory
List<String> list = LazyList.decorate( GrowthList.decorate( backingList ), new LazyListFactory( Arrays.asList("D","E","F","G") ) );

// Now puit "Z" in position 15
list.set( 15, "Z" );

System.out.println("With a Java 1.5 for-loop the results look like this." );
int i = 0;
for (String s : list)
{
System.out.println( " Element [" + i++ + "] = " + s );
}
}
}


Like every solution, there is a draw back to this method, when an element does not exist, the result is null. In this example, I print every element, including the nulls. Let's examine the output.

With a Java 1.5 for-loop the results look like this.
Element [0] = A
Element [1] = B
Element [2] = C
Element [3] = null
Element [4] = null
Element [5] = null
Element [6] = null
Element [7] = null
Element [8] = null
Element [9] = null
Element [10] = null
Element [11] = null
Element [12] = null
Element [13] = null
Element [14] = null
Element [15] = Z


Each solution I have covered has some form of a draw back. I hope you can find one suitable for you. I will be examining the LazyMap in my next installment; it is well suited for hierarchical data.


Thanks for reading this article, I look forward to any questions or feedback. Please drop me a line if you want me to cover something in particular or would like to point something useful out.
Author: Philip A Senger

Sunday, January 25, 2009

TransformedMap and Transformers: Examples of Strategy, Decorator, and Factory

I mentioned some software design patterns in my last two examples one was a Factory, Decorator, and the other a Strategy. Before we get into the next example, I want to make sure you understand these patterns because, clearly the developers at Apache where thinking of these patterns when they developed the Commons Collections API. Let’s talk about a Factory first.

Factories:
Factories are a subset of what is called creational design patterns (See the wikipedia Abstract factory pattern). There are a couple of variations on the factory pattern but basically, you ask the factory for a named object, it assembles it and returns it. Just like a factory, hence the name. This is the building block of a lot of my web applications.

Decorator:
A decorator pattern is simply a way of receiving one value and decorating it into something suitable for a different view. For example, if you have a bean and it has a value of 12.599999999 you might want to decorator it to say something like $12.54. The apache team decided that the TransformedMap.decorate method was actually decorating the key not really a strategy.

Strategy:
A strategy pattern (See the wikipedia Strategy pattern) is a subset of behavioral design patterns. This pattern is analogous to a socket wrench (the socket part not the wrench part). So, as you might know, the socket can be replaced with different sizes at anytime. It simply plugs into the wrench and can be turned right or left. The concept here is we create an interface (which is a contract or in the wrench analogy the part that connects the socket to the wrench) whereby algorithms can be selected and dynamically installed at runtime.

Personally, I feel the TransformedMap.decorate is a borderline case between a strategy and a decorator…. On one hand it decorates the values going into the key, but on the other hand it changes the behavior at run time...Also, Im using a MultiValueMap for the backing map which totally changes the behavior. So, I will refer to it as a Strategy. I know some people will take issue with this, oh well. Lets declare some simple objects, like the Gender and State enum.


package com.blogspot.apachecommonstipsandtricks;
public enum Gender
{
Male, Female
}



package com.blogspot.apachecommonstipsandtricks;
/**
* Official USPS Abbreviations
*/
public enum State
{
AL("ALABAMA"), AK("ALASKA"), AS("AMERICAN SAMOA"),
AZ("ARIZONA "), AR("ARKANSAS"), CA("CALIFORNIA "),
CO("COLORADO "), CT("CONNECTICUT"), DE("DELAWARE"),
DC("DISTRICT OF COLUMBIA"), FM("FEDERATED STATES OF MICRONESIA"), FL("FLORIDA"),
GA("GEORGIA"), GU("GUAM "), HI("HAWAII"),
ID("IDAHO"), IL("ILLINOIS"), IN("INDIANA"),
IA("IOWA"), KS("KANSAS"), KY("KENTUCKY"), LA("LOUISIANA"),
ME("MAINE"), MH("MARSHALL ISLANDS"), MD("MARYLAND"),
MA("MASSACHUSETTS"), MI("MICHIGAN"), MN("MINNESOTA"),
MS("MISSISSIPPI"), MO("MISSOURI"), MT("MONTANA"),
NE("NEBRASKA"), NV("NEVADA"), NH("NEW HAMPSHIRE"),
NJ("NEW JERSEY"), NM("NEW MEXICO"), NY("NEW YORK"),
NC("NORTH CAROLINA"), ND("NORTH DAKOTA"), MP("NORTHERN MARIANA ISLANDS"),
OH("OHIO"), OK("OKLAHOMA"), OR("OREGON"),
PW("PALAU"), PA("PENNSYLVANIA"), PR("PUERTO RICO"),
RI("RHODE ISLAND"), SC("SOUTH CAROLINA"), SD("SOUTH DAKOTA"),
TN("TENNESSEE"), TX("TEXAS"), UT("UTAH"),
VT("VERMONT"), VI("VIRGIN ISLANDS"), VA("VIRGINIA "),
WA("WASHINGTON"), WV("WEST VIRGINIA"), WI("WISCONSIN"), WY("WYOMING");
private String fullyQualifiedName;
State(String fullyQualifiedName)
{
this.fullyQualifiedName = fullyQualifiedName;
}
public String getFullyQualifiedName()
{
return fullyQualifiedName;
}
}


Ok, now define our Data Transfer Object (DTO), just like the last example.


package com.blogspot.apachecommonstipsandtricks;
public class DTO
{
private int id;
private String name;
private Gender gender;
private State state;

public DTO(int id, String name, Gender gender, State state)
{
this.id = id;
this.name = name;
this.gender = gender;
this.state = state;
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Gender getGender()
{
return gender;
}
public void setGender(Gender gender)
{
this.gender = gender;
}
public State getState()
{
return state;
}
public void setState(State state)
{
this.state = state;
}
@Override
public String toString()
{
return "com.blogspot.apachecommonstipsandtricks.DTO{id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", state=" + state + '}';
}
}


And now, ( drum roll please ) the factory.


package com.blogspot.apachecommonstipsandtricks;
import java.util.*;
import org.apache.commons.collections.map.*;
import org.apache.commons.collections.*;
public class MapFactory
{
public static Map getMap(MapFactoryEnum whichFactory)
{
Map returnMap = null;
if (null != whichFactory)
{
Transformer transformer;
switch (whichFactory)
{
case NAME:
transformer = TransformerUtils.invokerTransformer("getName");
break;
case STATE:
transformer = TransformerUtils.invokerTransformer("getState");
break;
case GENDER:
transformer = TransformerUtils.invokerTransformer("getGender");
break;
default:
throw new IllegalArgumentException("Unknown Map");
}
returnMap = TransformedMap.decorate( new MultiValueMap(), transformer, TransformerUtils.nopTransformer());
}
return returnMap ;
}
public enum MapFactoryEnum
{
NAME, STATE, GENDER
}
}

So, when you call getMap, you request a map by a name. The factory builds a new MultiValueMap and decorates it, as in a Strategy with the transformer.

The code:

package com.blogspot.apachecommonstipsandtricks;
import java.util.*;
import org.apache.commons.collections.*;
import org.apache.commons.lang.*;
public class TestMapFactory
{
public static void main(String[] args)
{
int i = 0;
List<dto> list = Arrays.asList(new DTO(i++,"Bob", Gender.Male, State.WI), new DTO(i++,"Larry",Gender.Male, State.WI),
new DTO(i++,"Bill", Gender.Male, State.WI), new DTO(i++,"Sue", Gender.Female, State.AZ),
new DTO(i++,"Joe", Gender.Male, State.AZ), new DTO(i++,"Zoe", Gender.Female, State.WI));

List<dto> dtosFromTheMap;
Collection names;
Map map;

System.out.println("------------------------- By State -------------------------");
map = MapFactory.getMap(MapFactory.MapFactoryEnum.STATE);

for (DTO dto : list)
{
map.put( dto, dto );
}

dtosFromTheMap = (List<dto>) map.get(State.WI);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are in " + State.WI.getFullyQualifiedName() );

dtosFromTheMap = (List<dto>) map.get(State.AZ);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are in " + State.AZ.getFullyQualifiedName() );

System.out.println("------------------------- By Gender -------------------------");
map = MapFactory.getMap(MapFactory.MapFactoryEnum.GENDER);

for (DTO dto : list)
{
map.put( dto, dto );
}

dtosFromTheMap = (List<dto>) map.get(Gender.Male);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are " + Gender.Male );

dtosFromTheMap = (List<dto>) map.get(Gender.Female);
names = CollectionUtils.collect(dtosFromTheMap, TransformerUtils.invokerTransformer("getName"));
System.out.println(StringUtils.join(names.iterator(),",") + " are " + Gender.Female );
}
}


This code could be very useful if you where caching items or grouping selections together inside a controller. Just a side note, maps are always non-thread safe. I will explain in my next example what that means.

The results:

------------------------- By State -------------------------
Bob,Larry,Bill,Zoe are in WISCONSIN
Sue,Joe are in ARIZONA
------------------------- By Gender -------------------------
Bob,Larry,Bill,Joe are Male
Sue,Zoe are Female

Author: Philip A Senger