Yesterday I was unit testing some code of my work and I started with a top down approach regarding the way I wanted to write the testing code.
Basically I wanted to have a collection of Command objects that would change my input instances (to a Rule Engine) so I could mix and match easily different updates to trigger different rules, at each unit test (lines 9-10).
I also wanted to make several requests to the Rule engine, before validate the results, since a lot of rules are just fired after other ones being fired on previous invocations (lines 12-13).
But what I wanted the most was to easily express the way I would get results from different iterations (using ranges); and summarize elements and apply operations on the resulting sets of data (lines 15-17).
02:{
03: @Test
04: public void execute()
05: {
06: Transaction t = new Transaction();
07: t.setAuthorizationDate(new Date());
08:
09: TransactionUpdateCommand mcc = new SetMccRamdomCommand(5001);
10: TransactionUpdateCommand codePosPDV = new SetTerminalCodePosPDV("xxxxxx12");
11:
12: RuleServiceInvoker commands = new RuleServiceInvoker();
13: CommandsResultSet results = commands.run(t, 10, mcc, codePosPDV);
14:
15: assertTrue(results.iteration(0, 4).intProfileVar(3).incrementedEachTime(1));
16: assertEquals(0, results.iteration(5).intProfileVar(3));
17: assertTrue(results.iteration(6, 10).intProfileVar(3).incrementedEachTime(1));
18: }
19:}
To better understand the assertions above one must know that some arrays of integers, double, Dates are sent back and forth to the Rule Engine, inside the engine the end user sees those array slots as proper named structured data, but at this level, where we poor developers reside they are just arrays, and the class that holds them all is the one called 'Profile', so now I can say that I wanted in the line 17:
- Extract the results from the iterations (or requests) 6 to 10;
- From those iterations work only with the integer array of values, specifically the ones on the position 3;
- Check if the difference between each one of those are of 1;
All those methods in place I could start with their implementation, and bellow is the result (with the omission of some unrelated code ;):
Here is the code to run the commands upon the input instances and an special mention to the inner private class 'LocalReturn' just because java does not have tuples or return statements with multiple values (of course one can always return an array, but it's not that beautiful).public class RuleServiceInvoker
{
CommandsResultSet run(Transaction t, int times, Command... commands)
{
CommandsResultSet results = new CommandsResultSet();
for (int i = 0; i < times; i++) {
...
command.execute(t);
}
input.setTransaction(t);
LocalReturn ruleServiceReturn = invokeAndKeep[OmittedName2]Variables(input);
results.add(ruleServiceReturn.output);
...
}
return results;
}
...
[OmittedName1]Input input;
[OmittedName1]Output output;
public LocalReturn([OmittedName1]Input input, [OmittedName1]Output output) {
this.input = input;
this.output = output;
}
}
}
Bellow the 3 container classes, so I could work exactly with the data I needed for each assertion:
public class CommandsResultSet { private List<ComandsOutputIteration> iterations = new LinkedList<ComandsOutputIteration>();Here an interesting spot where I could use a bit of recursion, at the 'diff' method:public ComandsOutputIterationSet iteration(int start, int end) { ComandsOutputIterationSet set = new ComandsOutputIterationSet(); for (int i = start; i < end; i++) { set.add(iterations.get(i)); } return set; } public ComandsOutputIteration iteration(int idx) { return iterations.get(idx); } public void add([OmittedName1]Output output) { iterations.add(new ComandsOutputIteration(output)); } } public class ComandsOutputIterationSet { private List<ComandsOutputIteration> iterations = new LinkedList<ComandsOutputIteration>();public IntProfileVarSet intProfileVar(int idx) { IntProfileVarSet set = new IntProfileVarSet(); for (ComandsOutputIteration it : iterations) { set.add(it.intProfileVar(idx)); } return set; } public void add(ComandsOutputIteration iteration) { iterations.add(iteration); } } public class ComandsOutputIteration { private [OmittedName1]Output output; public ComandsOutputIteration([OmittedName1]Output output) { this.output = output; }public int intProfileVar(int idx) { return output.get[OmittedName2]ProfileVariables().getIntegerValues()[idx]; } }
public class IntProfileVarSet { private List<Integer> ints = new LinkedList<Integer>(); public boolean incrementedEachTime(int diff) { if (ints.size() < 2) { throw new RuntimeException("Less than 2 elements! No increment can be verified!"); }return diff(0, 1, diff); }private boolean diff(int current, int next, int diff) { if (ints.get(next) - ints.get(current) == diff) {return next + 1 == ints.size() ? true : diff(next, next + 1, diff); } return false; } public void add(int intProfileVar) { ints.add(intProfileVar); } }
This top down approach to design certain operation is most pleasant since you already start exactly where you want to get in the end :-)
*The [OmittedNameX]s where placed to protect the Business of this client.
The code bellow shows how Java annonynous inner classes are just static typed closures, with all verbosity but also all behaviour needed :-)
2:
3:public class VerboseClosures {
4: public static void main(String[] args) {
5: MyList langs = new MyList();
6: langs.add("Java");
7: langs.add("Scala");
8: langs.add("Ruby");
9:
10: final String msg = "Got ya";
11: langs.each(new HandleAll() {
12: public void handle(String s) {
13: System.out.println(msg + ": [" + s + "]");
14: }
15: });
16: }
17:}
18:
19:class MyList extends LinkedList<String> {
20: void each(HandleAll handler) {
21: for (String s : this) {
22: handler.handle(s);
23: }
24: }
25:}
26:
27:interface HandleAll { void handle(String s); }
Back on 2004, Fowler said about closures:
So the first crucial point about closures is that they are a block of code plus the bindings to the environment they came from. This is the formal thing that sets closures apart from function pointers and similar techniques.(Java's anonymous inner classes can access locals - but only if they are final.)
As we can see on lines 10 and 13 our method being sent to the each operation is capable of accessing a local variable (marked as final though). Of course, closures should be much more easy to manually write, that's half of their strengh.
This is again, just to exemplify the concept, even so, anonymous inner classes are extremely handy in the absence of "normal" closures backed up by good syntax sugar.
(*) Extending LinkedList is not encourage, but this is just to make the example simpler
(**) In favour of Java I can say that free IDEs like Eclipse can generate all this verbose code with a few keystrokes (and programmers are more than used to that with their editors, hehe). Hence, leaving us the the best of both worlds, closures but still all the good side of static typing, like safe automated refactorings. And the chances you to be using already a IDE for Java development are rocket high...
(***) Closures are much more pleasant with proper syntax sugar, there's no way do deni it.
It's a live CD so will run just off the CD drive if you want to play with it. As with any of these live CDs you can then install it to your hard drive easily and you have the full server ready to go. There's also a VMWare Image.Apparently it comes with a Continuous Integration Server, a SCM (subversion I would guess), a Issue tracking and a wiki.
[1, [2, 3], 4, [5, [6, 7], 8], 9].flatten = [1, 2, 3, 4, 5, 6, 7, 8, 9]
And in the end I´ve ended up implementing it myself only a couple of days later of thinking that :)
I am doing some combinations of elements in my current project and my algorithm resulted this kind of collections, that may have elements that are other inner collections. All I needed was this kind of flatten operation, of course I could review my algorithm but this solution required much less effort. I guess the use of this operation is more common than I thought.
Here is the code:
public static Collection flatten(Collection elements)
{
Collection flattened = new LinkedList();
for (Object o : elements) {
if (o instanceof Collection)
flattened.addAll(flatten((Collection) o));
else
flattened.add(o);
}
return flattened;
}
A human interface operation is one that has a very specific usage but even though it is added to your interface against the philosophy of adding only operations of a general need.
By the way, Cedric has recently posted his opinions on human interfaces too.
Anyway, as a programmer I feel writing more readable code with API with lots of useful operations.
Did you know Visual Age for Java was written initially in Smalltalk?
"Yes. Eclipse is an open-source platform for building IDEs that was originally developed at IBM. It’s a descendant of their VisualAge for Java product. That product was originally written in Smalltalk."
A good definition of Refactoring:
"a refactoring is a “behavior preserving program transformation”."
"Of all the technologies in the program, I believe refactoring has then potential to produce a larger immediate benefit than any of the others"
I could not agree more.
"forward to refactoring Fortran to objects one day not to far in the distant future"
Object Oriented paradigm each time more recognized as a powerful software engineering tool.
Interesting to know a little about Fortran programming language evolution, and Eclipse plugins development. Warning: Not a technical reading at all.
Read on Fowler´s blog/wiki.
