Home

Advertisement

Back on 2005 I wrote about an practical use of what Fowler describes as "Human Interface" operations.

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

01:public class TerminalModelTest extends CommandTests
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++) {
            ...
            for (TransactionUpdateCommand command : commands) {
                command.execute(t);
            }

            input.setTransaction(t);
            LocalReturn ruleServiceReturn = invokeAndKeep[OmittedName2]Variables(input);
            results.add(ruleServiceReturn.output);
            ...
        }
        return results;
    }
    ...
    private class LocalReturn {
        [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>();

    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];
    }
}
Here an interesting spot where I could use a bit of recursion, at the 'diff' method:
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.

Framework/API Main Goal: Reuse

  • Mar. 22nd, 2009 at 4:15 PM
The main goals of the WebWork Framework depicted on the Wikipedia's article:
  1. Web Designer never has to touch Java code;
  2. Create multiple "Web Skins" for a application;
  3. Change Look and Feel;
  4. Change Layout on a given Web Page;
  5. Change Flow among Web Pages;
  6. Move *existing* data elements from one page to another;
  7. Integrate with various backend infrastructures;
  8. Reuse components;
  9. Perform internationalization (i18n) of a web application;
  10. Keep the API small and to the point;
  11. Ability to learn WebWork fast, by making all the fancier features optional;
  12. Allow the developer to choose how to implement as much as possible, while providing default implementations that work well in most cases [1];

I'm not sure if the order on the list above reflects the priority but if so I would strongly use the items from 8 until 12 as the first top one priorities:

  1. Reuse components;
  2. Keep the API small and to the point;
  3. Ability to learn <YOUR FRAMEWORK/API NAME HERE> fast, by making all the fancier features optional;
  4. Allow the developer to choose how to implement as much as possible, while providing default implementations that work well in most cases [1];
  5. Perform internationalization (i18n) of a web application;

An the last item is for certain one of the main strengths of the Springframework, as well as the terrific usage of Object Oriented advantages as Polymorphism.

I stumble upon this WebWork article because a tool I'm using for the past months makes some use of it :)

An interesting defense of software modeling on this post by Grady Booch:

a) the most important artifact of any software development organization is executable code and yet b) modeling is essential in constructing such executables. This is because c) models help us reason about, specify, construct, and document software-intensive systems at levels of abstraction that transcend source code (and the UML is the accepted open standard for doing so). That being said, it is a pragmatic reality that d) some models are essential (and should be retained) while others are simply scaffolding (and should be discarded).

Tags:

"Human Interface" operations

  • Dec. 9th, 2005 at 5:35 PM
By coincidence today I just had to implement my last post flatten operation of Array Ruby class. What is is funny about it is that when I read Fowler using it as an example of a typical operation of a human interface operation I thought to myself: "Who on earth would need such an operation?" If you don´t know what it does here is a example (Ruby code):

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

Fowler has an interesting wiki entry on design of API interfaces. Although I think he missed a point on talking about java.util.List interface lack of more friendly operations (like last(), first()...) and not considering the java.util.LinkedList class interface which has much of those operations. Even though nothing so friendly (or human if you rather saying) like Ruby´s Array interface operation flatten mentioned in the article, and many others I suppose.

Anyway, as a programmer I feel writing more readable code with API with lots of useful operations.

Prefactoring

  • Oct. 13th, 2005 at 11:14 AM
O´Reilly is publishing a new book entitled 'Prefactoring' by Ken Pugh.

"In his new book, Prefactoring (O'Reilly, US $29.95), author Ken Pugh shares practices and guidelines derived from his own experiences and those of many other developers. These guidelines make explicit the considerations that good developers implicitly make in their designs. "Take these guidelines as a starting point to developing your own," Pugh advises readers. "Many of the guidelines explored in the book relate to basic design principles, but they are expressed in different fashions. Other guidelines revolve around the concepts of Extreme Abstraction, Extreme Separation, and Extreme Readability."

Seems to be a architectural software design book that has nothing related to refactoring, but still seems interesting.

Latest Month

November 2009
S M T W T F S
1234567
891011121314
15161718192021
22232425262728
2930     

Tags

Syndicate

RSS Atom
Powered by LiveJournal.com
Designed by Tiffany Chow