Tuesday, May 25, 2010

Glimpses of Glorious Moose Traits

Anything that makes a software developer's life easier is good. Moose 'traits' fits right in that category.

A diversion .. When I first came upon Perl, what impressed me most was the ease with which data structures could be constructed, manipulated and extended. The ease of stuffing any kind of data - whether an object, array or even another hash - into a hash was a simplification that made coding a breeze! It is features like this in programming languages that free-up a developer's mental infrastructure from book-keeping to creative implementation - so s/he can focus on shaping an application rather than mental retention of myriad mettlesome minutiae.

But therein lies the bug-feature rub, if you catch my drift. This feature extracts a price. You've heard it, don't make me say it - Perl is slow. Perl is a memory-hog. And the inevitable comparisons to to C++ and Java. Needless to say, having more than one tool in one's belt is essential - nay, indispensable!

I can take comfort in that. You see, I love Taekwon-do, am devoted to this Korean martial art, and my sa-bum tells me "To excel in Taekwon-do, you must know Judo." Or Aikido. Or La Savate. Or Muy Thai. Or Rifle Shooting. The point is, unless you immerse yourself into the 'other' side, you risk getting locked into patterns of thinking that ultimately crimp your style.

So hear ye, developers, and be comforted. When slapped with criticism, turn the other cheek. Don't bash Java. Resist the temptation to ridicule .Net. Rather, learn about them! Then, when faced with an interesting problem, make the best choice. Yes, life is tough and there is no other way but to keep at it.

I must say though, as one having ploughed my way through C, C++ and .Net, I know of no other language that gives me as much creative freedom as my beloved Perl. Coding in Perl, particularly with the MOOSE object-oriented framework, is on par with other pleasures in life like watching sunrise from a mountain top or making love to a beautiful woman or making love to a beautiful woman at sunrise on a mountain top.

And with that sanguine thought, onward to traits.

Consider the Remote Control class from the previous example. (See: Command Pattern.) The Remote, which is an 'Invoker', is composed of 'Commands', each of which wraps around a 'Receiver', wherein resides behavior and the methods to access it. Dense though this clap-trap is, cutting to the chase a class is composed of other classes in sets. Savvy?

Consider the typical implementation.

package RemoteControl;
use Moose;

use Command;


has 'onCommands' => (
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
);

has 'offCommands' => (
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
);

Consider access to the slots in the BUILD method.

sub BUILD {
my $self = shift;
for (my $i = 0; $i < 7; $i++) {
push @{$self->onCommands}, NoCommand->new;
push @{$self->offCommands}, NoCommand->new;
}
}

A bit clumsy, don't you think? The sub (which is a method, by the way) unpacks the class to get to the underlying slot. Not a good thing. And this has to happen each time the slot is accessed as a container - to push/pop, count, store at/retrieve from an indexed location, sort, filter via map/grep, etc. Is there a way one could get a handle on this rum business?

There is. Consider the implementation using traits. See the rum.. err, handle?

package RemoteControl;
use Moose;
use Command;


has 'onCommands' => (
traits => ['Array'],
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
handles => {
all_onCommands => 'elements',
add_onCommand => 'push',
map_onCommands => 'map',
filter_onCommands => 'grep',
find_onCommand => 'first',
get_onCommand => 'get',
join_onCommands => 'join',
count_onCommands => 'count',
has_onCommands => 'count',
has_no_onCommands => 'is_empty',
sorted_onCommands => 'sort',
},
);

has 'offCommands' => (
traits => ['Array'],
is => 'rw',
isa => 'ArrayRef[Command]',
default => sub { [] },
handles => {
all_offCommands => 'elements',
add_offCommand => 'push',
map_offCommands => 'map',
filter_offCommands => 'grep',
find_offCommand => 'first',
get_offCommand => 'get',
join_offCommands => 'join',
count_offCommands => 'count',
has_offCommands => 'count',
has_no_offCommands => 'is_empty',
sorted_offCommands => 'sort',
},
);

This is what access to containers now looks like, with traits involved:

sub BUILD {
my $self = shift;
for (my $i = 0; $i < 7; $i++) {
$self->add_onCommand(NoCommand->new);
$self->add_offCommand(NoCommand->new);
}
}

Amen!

Monday, May 24, 2010

The Command Pattern - SDLC

Ch. 6 from HFDP - Part 03 of 03

Target Audience: This series is pitched at developers interested in Object Oriented Design Patterns using the Moose framework in Perl. I cover the examples from the Head First Design Patterns book, replicating Java implementation in Moose. You need to obtain a copy of the book, which is not hard to come by.

How do you write code?

If you are like most software developers in a typical software shop, you write code the way you know to write code. Then you test it. Right? Or sometimes, you skip the tests. I mean, with all the constant pressure to put out code ..

How about a way to code which automatically generates tests and produces all the documentation you ever need? What is it worth to you? What if, this way also improves software quality and reduces the time duration for upgrades in the development life-cycle through the life of the software? What is it worth to you then?

There is a way, and like many other worthwhile things in life, all it requires is commitment to the process. The way consists of building an application starting with the tests, then write code to tests. On this side of the East-West divide, we say that all great truths are paradoxical. So writing tests and then coding to them is turning convention on the head. But it works. And you won't know until you do it. As Yoda tells the disbelieving young Jedi in "Empire Strikes Back" - Try not. Do. Or do not. There is not try.

It gets even better in a paired programming partnership, where one developer writes the tests and the other writes the code to those tests. Then, they switch roles. I have pair-programmed with programmers a level above me and a level below me. In both instances, I have improved my skills - learning new tricks from the better developers in our midst and from the questions of the less-skilled developers.

Mind you, paired programming is not a one-size fits all solution. There will always be lone gun-(wo)men. If you are in my position as the Head of a Software Development Group with a mix or people, personalities and cultures - it is best to open up paired-programming as an additional option that developers can easily avail of. And leave the choice to them. It helps to be flexible in these matters and keep an open mind.

Back to code. So what would the application test suite for the Remote Control look like? Let's start with the low-level APIs - the so-called Receiver classes. The code snippets illustrate sample tests for the Ceiling Fan and Hot Tub. First, the Fan, since it is kinda hot in here. (Or is it just me?)

print "--[ Receiver: Ceiling Fan ]--------------------------------------------";
my $myCeilingFan = CeilingFan->new;
isa_ok($myCeilingFan, 'CeilingFan');
$myCeilingFan->low;
is($myCeilingFan->state, 'low',
q{.. Set ceiling fan ON LOW});
$myCeilingFan->medium;
is($myCeilingFan->state, 'medium',
q{.. Set ceiling fan ON MED});
$myCeilingFan->high;
is($myCeilingFan->state, 'high',
q{.. Set ceiling fan ON HIGH});
$myCeilingFan->off;
is($myCeilingFan->state, 'off',
q{.. Set ceiling fan OFF});

This generates output like:


Now the 'ot tub:

print "--[ Receiver: Hot Tub ]------------------------------------------------";
my $myHotTub = HotTub->new;
isa_ok($myHotTub, 'HotTub');
$myHotTub->on;
is($myHotTub->state, 1,
q{.. Hot tub is on});
$myHotTub->off;
is($myHotTub->state, 0,
q{.. Hot tub is off});
$myHotTub->on;
$myHotTub->circulate;
$myHotTub->temperature(50);
is($myHotTub->temperature, 50,
q{.. Hot tub temperature is set to 50 degrees});
$myHotTub->temperature(150);
is($myHotTub->temperature, 150,
q{.. Hot tub temperature is set to 150 degrees});
$myHotTub->temperature(90);
is($myHotTub->temperature, 90,
q{.. Hot tub temperature is set to 90 degrees});

Which generates the output:


Quite a screenful, eh? Nothing too sophisticated though. All we are doing is testing class labels with 'isa_ok' and get/set (i.e. accessor/mutator) methods with 'is'. So what's the big deal?

Let's examine the output.

First, note how the documentation is implicit. The 'isa_ok' tests provide a good opportunity to explain the constructor calls. The 'is' tests present an opportunity for one-liners as succinct documentation of the get/set methods - at least the important ones. Any other methods can be tested - and documented - in the same manner.

Note that these tests are based on Perl's utilitarian 'Test::More' package. (The 'use' declaration is not shown in the snippets posted above. Refer to the code for these details.) Some handy utilities from the 'Test::More' package are 'like' for tests based on a pattern match, 'can_ok' to test if an object or class has a method, 'is_deeply' for validating data structures with XML-style nested organization.

The Command APIs are similarly tested. Take a look at the Ceiling Fan - being a state machine, its tests are interesting!

print "--[ Command: Ceiling Fan Hi / Lo / Med / Off]--------------------------";
my $myTestCeilingFan = CeilingFan->new;
print "-----------------------------------------------------------< ON HI >---";
$myTestCeilingFan->medium; # fan set randomly at medium
is($myTestCeilingFan->state, 'medium',
q{Fan is ON MED at start.});
my $myCeilingFanHighCommand =
CeilingFanHighCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanHighCommand, 'CeilingFanHighCommand');
$myCeilingFanHighCommand->execute;
is($myCeilingFanHighCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via Command.});
$myCeilingFanHighCommand->undo;
is($myCeilingFanHighCommand->ceilingFan->state, 'medium',
q{Fan (re)set ON MED via undo.});
print "----------------------------------------------------------< ON MED >---";
$myTestCeilingFan->low; # fan set randomly at low
is($myTestCeilingFan->state, 'low',
q{Fan is ON LO at start.});
my $myCeilingFanMediumCommand =
CeilingFanMediumCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanMediumCommand, 'CeilingFanMediumCommand');
$myCeilingFanMediumCommand->execute;
is($myCeilingFanMediumCommand->ceilingFan->state, 'medium',
q{Fan (re)set ON MED via Command.});
$myCeilingFanMediumCommand->undo;
is($myCeilingFanMediumCommand->ceilingFan->state, 'low',
q{Fan (re)set ON LO via undo.});
print "----------------------------------------------------------< ON LO >---";
$myTestCeilingFan->high; # fan set randomly at high
is($myTestCeilingFan->state, 'high',
q{Fan is ON HI at start.});
my $myCeilingFanLowCommand =
CeilingFanLowCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanLowCommand, 'CeilingFanLowCommand');
$myCeilingFanLowCommand->execute;
is($myCeilingFanLowCommand->ceilingFan->state, 'low',
q{Fan (re)set ON LO via Command.});
$myCeilingFanLowCommand->undo;
is($myCeilingFanLowCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via undo.});
print "-------------------------------------------------------------< OFF >---";
$myTestCeilingFan->high; # fan set randomly at high
is($myTestCeilingFan->state, 'high',
q{Fan is ON HI at start.});
my $myCeilingFanOffCommand =
CeilingFanOffCommand->new(ceilingFan => $myTestCeilingFan);
isa_ok($myCeilingFanOffCommand, 'CeilingFanOffCommand');
$myCeilingFanOffCommand->execute;
is($myCeilingFanLowCommand->ceilingFan->state, 'off',
q{Fan (re)set OFF via Command.});
$myCeilingFanOffCommand->undo;
is($myCeilingFanLowCommand->ceilingFan->state, 'high',
q{Fan (re)set ON HI via undo.});

Notice how each Command is given a starting state that is changed upon invocation of the 'execute' method, and restored with 'undo'? Nifty!



One can continue on in this manner. For this example, the Invoker (i.e Remote) is tested by means of a dry-run. See the code for details.

Epilogue:
Unit-testing in this way looks tedious and time-consuming. Yes, it can be that. Particularly if code is written first and tests later. That's why it is very, very, very, very, very, very, very, very, very, very, very important to write the tests first. Web-Application Frameworks in Perl such as Catalyst are designed with this approach in mind.

Writing tests first gives rise to lots of questions. That's because the coder is likely to ask the tester about the architectural features of the system as he/she gets their mind around the design. Perhaps there are some inconsistencies of the architecture that come to light. This way, there is a far greater chance that difficulties are noticed early, before a lot of code is written. That can be majorly beneficial.

Yet another benefit is that there is always a benchmark for changes made during the life-cycle of the application. The test-suite, which is continually updated, serves as a basis for regression testing.

For example, while writing this little application, I decided to upgrade the Remote Control class implementation with traits - a feature of Moose 1.05+. After working this into the code, I immediately ran the application test suite to ensure nothing was broken. Sure enough, things were mis-shapen. But with regression testing to guide the development process, it the process was far easier.

Always built testing into your process. It is key to developing an application incrementally, in small and consistent steps. Now that's bringing Kaizen to the software world.

All code in this series may be downloaded from:
http://sites.google.com/site/sanjaybhatikar/codeunquote/designpatterns-1