The LiveScribe pen allows penlets (pen applications) to be written in J2ME (Java 2 Micro Edition) and deployed onto the pen. There are not a lot of penlets around yet, so I thought I would share a few personal opinions on how to structure penlets well, based on early penlet writing experience (and years of commercial programming background).
Anatomy of a Simple Penlet
A penlet has a main class that implements the Penlet interface. It has methods such as initApp(), activeApp(), deactivateApp() and destroyApp() which are called at different times during the pen’s lifecycle.
In simple applications, it is also common for the main class to implement several other interfaces and then register itself as a “listener” for specified types of events. Examples of events are pen on/off paper, a new stroke has been written on paper, and a menu item has been selected. The advantage of the one class implementing multiple interfaces like this is it makes it trivial to share variables between all the different event handlers and the lifecycle context.
It is important to understand that penlets are event driven. They get called when something happens. Like web applications and other event based systems, a penlet cannot display a prompt then sit and wait for the user’s response. Instead, after displaying a prompt, the penlet must then return back to the pen OS. When some event intended for the application occurs, the application is called to process the event. The penlet is therefore continually reacting to events (hence the name “reactor” in the design pattern literature). It remembers state between events in class fields.
For small penlets, having a single class with all the code in it is fine. However, as the penlet grows, this approach soon becomes problematic. In particular, the logic starts getting more complex and confusing and automated testing is difficult. Logic complexity and automated testing are addressed separately below.
Penlet versus Application Events
The issue with logic complexity is in practice then pen events are not exactly what the application is typically after. For example, imagine an application where if you draw some text it parses the text written (using the handwriting recognition software built into the pen) and creates a “button” for it. Later if you touch the text (the “button”) then it invokes that operation. It is almost impossible to put the pen onto the page without a slight movement, so in both cases you will get pen up/down event(s) – possibly several during the writing phase – and stroke events. However the application really wants to know when a button was created, and when an existing button was tapped. In this case, the application code would be cleaner if the application had newButton() and buttonTouched() methods that were called at the appropriate times.
Diving deeper here, pen down and up events are triggered first, followed by a stroke event. The handwriting subsystem accepts strokes until the user pauses, which time it hands its best attempt at parsing the writing to the application. If an existing “button” is tapped, the pen down event includes an ID for the region that was touched. In this case there is no need (or desire) to pass the stroke on to the handwriting subsystem. So the “new stroke” event should do nothing if after a pen down over an existing region. If new text was written, then when capture is complete a new region would be registered to detect later touches on it. In this case the new stroke event should do some work. The point is, the penlet events may not directly align with what the application wants to receive. Some code may be desirable to map penlet events into application events, clarify the overall application structure.
Automated testing is important for all applications (not just penlets). Automated tests allow an application to be modified with confidence that existing functionality has not been broken. When a bug in code is found, I always recommend writing a unit test first (demonstrating the code is broken) then fix the problem. The unit test typically helps when fixing the problem. Then you also have a unit test to make sure the same bug never comes back again.
The problem with automated testing for penlets is the number of LiveScribe classes that only exist on the pen itself that are referenced from code. This includes events passed into the penlet, but also includes outputs such as playing of sounds or updating the pen display.
Given that it is currently difficult to impossible to test the LiveScribe specific logic, the next best approach is to split the application into LiveScribe specific code (code that can reference LiveScribe classes) and application specific code (code that never references LiveScribe classes). The application code can then be unit tested using junit or similar.
The challenge is how to decouple the code.
Having a Penlet class call the application code with special application events is trivial. All required data can be copied out of LiveScribe classes and into application data structures.
Having the application code output data back to the LiveScribe classes is harder. The best approach is for the application to define interfaces for callback methods it requires. For example, there may be a updatePenDisplay() method defined in an interface. The Penlet main class can then provide implementations of any required application interfaces to the application class. Unit tests would supply a different implementation of the callback interfaces allowing the tests to capture the output for verification. This does result in more overall code (various interfaces with two implementations – the real code and test infrastructure code), but it does make unit testing possible (of the application code).
Is it Worth It?
For simple applications, introducing the separation between LiveScribe specific code and application code that makes no reference to any LiveScribe class is probably overkill.
However, as soon as automated testing is wanted, splitting the code is strongly recommended. It also helps with the application code clarity as the application code is able to be defined in terms of events it wants to receive. Having code in pen up/down etc methods for all application purposes can result in spaghetti code pretty quickly.