Wednesday, December 03, 2008

IF-LESS code : State / Strategy Pattern

We have a simple class that is looping on a given List. Requirement are :

1) Service must preserve the first value of the list;
2) Each other element must be printed in uppercase
3) At the end of the loop the "Exit" string must be printed out;

The first version is the following :

public class Service {
String theFirst;
public void loop(List strings){
int index = 0;
for (String string : strings) {
if (index == 0) {
theFirst = string;
} else {
System.out.println(string.toUpperCase());
}
index++;
}
System.out.println("Exit");
}
}


Ok, brief and (moreless) clear; Let's try to modify a little bit that class; First, I'd introduce a compose method in order isolate what needs to be done 
for middle iterations (uppercase printing) so I'll introduce :

private void uppercase(String value) {
System.out.println(value.toUppercase());
}

Next, I don't like the explicit loop & condition logic. The tasks that need to be executed in the current example are very simple but let's try to imagine a more complicated task 
(for initial and middle iteration) : the code should result complicated to understand...

 I'd prefer to isolate those tasks in a separate method / class.
So, in order to do that, let's introduce an Iteration interface that represents the task that needs to be executed on a given iteration.

// Determines what needs to be done on a given iteration
interface Iteration {
void execute(String value);
}

we have two concrete implementors; something like that:

Iteration firstIteration = new Iteration() {
public void execute(String value) {
// Store the value of the first element
}
}

Iteration middleIteration = new Iteration() {
public void execute(String value) {
uppercase(value);
}
}

Now, each task should be responsible to execute its logic and in addition should provide some mechanism to compute & execute the next iteration. 
In order to do that we change a little the Iteration interface and the hosting class introducing a new Iterator member that is initialized on the loop() method.


interface Iteration {
void execute();
}

Iteration firstIteration = new Iteration() {
public void execute() {
// Store the value of the iterator.next() that is the first element of the list;
}
}

Iteration middleIteration = new Iteration() {
public void execute() {
uppercase(iterator.next());
}
}

private Iterator iterator;

...

public void loop(List strings) {
iterator = strings.iterator();
}


At this point we need to maintain a reference to the current iteration. So let's introduce another instance member :

private Iteration currentIteration = firstIteration

The initial value of this currentIteration is obviously the previously declared inner class "firstIteration". After that the loop method should be changed in order to start the loop : 

public void loop(List strings) {
iterator = strings.iterator();
currentIteration.execute();
}

Next, we need a way to continue the execution of the loop. We can do that introducing another method :

public void nextIteration() {
currentIteration = (iterator.hasNext()) ? middleIteration: noIteration;
currentIteration.execute();
}
ok, ok, I know...this is still a conditional logic but is not exaclty the same as the previous one...I'm not deal with indexes or temporary variables...just with the given collection...
As you can saw, we need another instance of Iteration inner interface for determine what needs to be done when the end of the loop is reached.

private final Iteration noIteration = new Iteration() {
public void execute() {
System.out.println("Exit");
}
}

That's all! Do you think I'm paranoic? :)....mmmmmm...yes! Note that the code above is just an exercise and the benefits are not evident because the starting class is very simple (I mean, the logic of the initial version of that class) but I can ensure you that they are more evident when those things are applied to a more complicated scenario...

This is the final version of the IlLessService :


public class IfLessService {
String theFirst;
private ListIterator iterator;
private interface Iteration {
void execute();
}
// What needs to be done in the first iteration?
private final Iteration firstIteration= new Iteration() {
public void execute() {
theFirst = iterator.next();
executeNextIteration();
}
};
// And in the middle iteration(s)?
private final Iteration middleIteration = new Iteration() {
public void execute() {
uppercase(iterator.next());
executeNextIteration();
}
};
// And when the iteration will terminate?
private final Iteration noIteration = new Iteration() {
public void execute() {
System.out.println("Exit");
}
};
private Iteration currentIteration = firstIteration;
public void loop(List strings){
iterator = strings.listIterator();
currentIteration.execute();
}
private void uppercase(String aString) {
System.out.println(aString.toUpperCase());
}
private void executeNextIteration(){
currentIteration = (iterator.hasNext()) ? middleIteration: noIteration;
currentIteration.execute();
}
}

Regards,
Andrea

No comments: