The Support Pattern

January 31, 2009

Today I thought I might write about a little pattern that I like to call the support pattern. It’s probably got some other name, or exists as a composite of one of the original GoF patterns but I always refer to it as the support pattern. The reason I call it the support pattern is because of the Java PropertyChangeSupport class. If you’re not familiar with it, the PropertyChangeSupport class provides a simple means for POJOs to fire an event when a property value changes. Clients can then subscribe to these change events and decide what to do with them. This is particularly useful in MVC type scenarios where changes in the Model need to be propagated to the View. Really when it comes down to it the support pattern is best employed in situations where you want to augment an existing object with some commonly used behavior that doesn’t necessarily fall within what you would consider to be the object’s primary purpose (remember the single responsibility OO design principle).

Anyway last night I found myself needing to add tagging behavior to my Transaction and BudgetItem objects in my personal finance app Freedom. Being that I had to add this behavior in more than one place it was an immediate candidate for some level of encapsulation and/or abstraction. Say hello to the support pattern!

The ITaggable interface

First I created an interface that defines what a taggable object can do.

ITaggable.java

/**
 * Interface for tagging objects.
 *
 * @since 30/01/2009 7:36:24 AM
 * @revision $Revision$ $Date$
 * @author Dave Kuhn
 *
 */
public interface ITaggable {

	/**
	 * Each taggable object that has at least one tag set has a primary tag. The
	 * primary tag is usually the first tag set via {@link #tag(String)} or
	 * {@link #tag(String[])}. Otherwise the primary tag can be set explicitly
	 * via {@link #setPrimaryTag(String)}.
	 *
	 * @return the primary tag
	 */
	String getPrimaryTag();

	/**
	 * Returns all the tags associated with this object.
	 *
	 * @return all tags for this
	 */
	String[] getTags();

	/**
	 * Returns true if this object has the specified tag.
	 *
	 * @param tag the tag to check
	 * @return true if object has tag, otherwise false
	 */
	boolean hasTag(String tag);

	/**
	 * Removes the specified tag from this object. This has no effect if the tag
	 * does not exist on the object.
	 *
	 * @param tag the tag to remove
	 */
	void removeTag(String tag);

	/**
	 * Removes the specified tags from the object. This has no effect if the any
	 * or all of the tags in the array do not exist for this object.
	 *
	 * @param tags the tags to remove
	 */
	void removeTags(String[] tags);

	/**
	 * Sets the primary tag for this object. If the tag does not exist, it is
	 * added to the greater list of tags for this object. If it already exists
	 * then the matching tag is simply set to be the primary tag.
	 *

	 * Where a primary tag has already been set (e.g. via the first call to
	 * {@link #tag(String)}) then setting the primary tag simply relegates the
	 * incumbent tag to being a regular tag, it does not remove it.
	 *
	 * @param tag the tag to set as the primary tag
	 */
	void setPrimaryTag(String tag);

	/**
	 * Tags this object with the specified tag.
	 *
	 * @param tag the tag to apply to this
	 */
	void tag(String tag);

	/**
	 * Tags this object with the specified tags.
	 *
	 * @param tags an array of tags to apply to this
	 */
	void tag(String[] tags);

}

The TagSupport class

The next step was to create a support class that implemented the ITaggable interface. This encapsulates all the necessary knowledge of how to tag an object.

TagSupport.java

/**
 * Utility class that provides tagging support for objects implementing the
 * {@link ITaggable} interface.
 *
 * @since 30/01/2009 6:52:28 PM
 * @version $Revision$ $Date$
 * @author Dave Kuhn
 */
public class TagSupport implements ITaggable {

	private String primaryTag;
	private List tags;

	/**
	 * Constructs a TagSupportProvider initialised with an empty tag list.
	 */
	public TagSupport() {
		tags = new ArrayList();
	}

	/**
	 * Constructs a TagSupportProvider initialised with the tags provided.
	 *
	 * @param tags tags to add
	 */
	public TagSupport(String[] tags) {
		this();
		tag(tags);
	}

	@Override
	public String getPrimaryTag() {
		return primaryTag;
	}

	@Override
	public String[] getTags() {
		return tags.toArray(new String[tags.size()]);
	}

	@Override
	public boolean hasTag(String tag) {
		return tags.contains(tag);
	}

	@Override
	public void removeTag(String tag) {
		// Ignore nulls, empty strings and tags which aren't in list.
		if (tag == null || tag.isEmpty() || !hasTag(tag))
			return;

		tags.remove(tag);

		/* First tag in list becomes primary tag in the event that tag being
		 * removed is the primary tag. If the item being removed is the last
		 * in the list (i.e. there are no tags left) the primary tag is set to
		 * null. */
		if (primaryTag.equals(tag) && tags.size() > 0)
			primaryTag = tags.get(0);
		else
			primaryTag = null;
	}

	@Override
	public void removeTags(String[] tags) {
		for (String tag : tags)
			removeTag(tag);
	}

	@Override
	public void setPrimaryTag(String tag) {
		if (!hasTag(tag))
			tag(tag);

		primaryTag = tag;
	}

	@Override
	public void tag(String tag) {
		// Ignore nulls, empty strings and duplicates.
		if (tag == null || tag.isEmpty() || hasTag(tag))
			return;

		// First tag becomes primary tag by default.
		if (tags.size() == 0)
			setPrimaryTag(tag);

		tags.add(tag);
	}

	@Override
	public void tag(String[] tags) {
		for (String tag : tags)
			tag(tag);
	}

}

Putting it to work

Lastly I added a TagSupport instance to my Transaction and BudgetItem classes, implemented the ITaggable interface, and then delegated to my member TagSupport instance like so:

Transaction.java

/**
 * A stateful transaction implementation.
 *
 * @since 30/01/2009 9:54:00 PM
 * @version $Revision$ $Date$
 * @author Dave Kuhn
 */
public class Transaction implements ITaggable {
	...

	private ITaggable tagSupport = new TagSupport();

	...

	@Override
	public void tag(String tag) {
		tagSupport.tag(tag);
	}

}

That’s all there is to it!

Zombies Ahead

January 25, 2009

Just found an funny article on hacking electronic road signs. I can’t count the amount of times I’ve driven past those things and fantasized about putting something amusing up. 

Hilarious; nice work guys.

Well it’s been nearly a month since I first wrote about project freedom (as in financial) and I thought it might be time give a brief update on where I’m at. Given that we’ve just had Christmas and I’m currently on holidays progress hasn’t been all that dazzling. That said, I’m still pretty happy with what has been done all things considered. So what have I been doing? Well so far I’ve mainly been looking at different approaches to building the application.

The Java Way

Naturally I immediately jumped to looking at a Java based solution – after all it is what I’d call my “home” language. Anyone who’s been around a while and has had the opportunity to program in Java as well as a variety of other programming languages/paradigms would undoubtedly agree that it is a beautiful language. I for one really appreciate the consistency of the syntax, the uncompromising enforcement of the OOP paradigm, and most of all the rich standard library with its accompanying documentation.

Being that I work with this stuff quite a bit my head was filled with all sorts of ideas about how we could achieve our goals using Java as the weapon of choice. The first thing that came to mind was that I could utilize the Eclipse RCP and JFace to get some quick wins getting a basic application framework up and running. This approach has the following distinct advantages:

  1. Eclipse RCP contains an OSGi container built in (Equinox to be exact) which is a very powerful plugin framework in a manner of speaking. Potentially it means that each discrete piece of functionality can be build as a separate plugin (or bundle in OSGi-speak).
  2. SWT/JFace contain a rich component set which would do about 95% of what I require and look/behave just like native components on Mac, Windows and Linux.
  3. RCP comes with a tool that allows to generate an executable and budle a JRE with the application so you can avoid “but my system doesn’t have Java (or version required) installed” type support headaches. It just runs.
  4. It’ll run on Mac, Windows, and Linux with no issues whatsoever.

So why wouldn’t I take that approach?

  1. Personally I’ve found RCP very hard to come to grips with. Documentation is scarce and outdated from what I can determine.
  2. OSGi adds a large amount of overhead in terms of complexity. Now I’m not exactly the smartest guy going round, but I’m not a moron either. I’ve spent hours frigging around with manifests, configurations, interfaces, dodgy docos and classpath hassles getting very little done. It all feels like a tremendous waste of time at this stage.
  3. Need to learn and ORM mapping tool like Hibernate or similar to overcome the OO to Relational mapping mismatch. I’m predominantly a user interface specialist so I usually leave this kinda stuff to someone else. Alternatively I can just battle on through with JDBC (*shudder).

As far as abstractions go Donald Knuth is reputed to have once said:

“There is no complexity problem in programming that cannot be eased by adding a layer of indirection. And there is no performance problem in programming that cannot be eased by removing a layer of indirection.”

Ordinarily I’d agree with this statement. You only have to look as far as something like an ORM to see this principle at work. What could be simpler than just working with your domain objects and letting something else worry about the whole persistence issue? On the contrary though I’m inclined to say that the sheer number of abstractions present in the aforementioned solution actually add to its complexity – not reduce it as promised. As always in Java I seem to be forced to spend a lot of time thinking about the correct way to do something as opposed to just doing it and refactoring later.

So there it is, I’ve spent a whole lot of time without a great deal of success. About the best thing it’s got going for it so far is that I’ve managed to do some modelling of the domain level objects. So where do we go from here?

Back to the Future

Some might consider this a backward step from comfortable walled garden that is Java but I’ve been experimenting a bit lately with C and GTK+. Yes you read right C, not C++, just pure unadulterated structured programming. Why not C++ indeed.

I’ll probably get flamed for saying this, but I just don’t like it. With great power comes great responsibility, and it’s like a wild beast that can take on any form it likes depending on the wild mood swings of your average programmer. Every time I read C++ I get the impression that whoever’s driving the evolution of the language can’t quite decide what they want it to be. If Java is a well manicured, if somewhat anal, golfing green then C++ resembles a hoarders front yard. You know the ones, full of old car bodies and useless junk left over after you take all the useful stuff out. Don’t take my word for it though, ask Linus. That said you can always pick and choose the bits you like and leave out the bits you don’t.

Anyway, so far I’m really impressed. Previously I’d thought that leaving OO behind would just be too painful however I was pleasantly surprised. To demonstrate a few concepts to myself I briefly knocked up this little demo which consists of a simple GTK+ interface sitting atop an SQLite database. As far as I’m concerned this a great “thin line” of technology from top to bottom which could conceivably be employed to write the app.

db_test.c

#include <stdlib.h>
#include <stdio.h>
#include <gtk/gtk.h>
#include <sqlite3.h>

/* Database file name. */
const gchar *DB_FILENAME = "test.db";

/* A simple SQL query to select all rows from the person table. */
const gchar *DB_QUERY = "SELECT * FROM person;";

/* Index of the name column in the list view */
const gint LIST_COL_NAME = 0;

/* Index of the age column in the list view */
const gint LIST_COL_AGE = 1;

/* The total number of columns in the list view */
const gint LIST_NUM_COLS = 2;

/* Global reference to DB handle. */
static sqlite3 *db;

/* Callback that processes a single row returned from the database. The first
 * parameter is expected to be a pointer to a GtkTreeView. */
static int
process_db_results( gpointer  data,
                    gint      ncols,
                    gchar    *col_values[],
                    gchar    *col_names[] )
{
    GtkTreeView    *view;
    GtkListStore   *store;
    GtkTreeIter     iter;

    /* Get reference to underlying GtkListStore so we can add to it. */
    view = GTK_TREE_VIEW (data);
    store = GTK_LIST_STORE (gtk_tree_view_get_model (view));

    /* Append a row and populate it using the row data provided */
    gtk_list_store_append (store, &iter);
    gtk_list_store_set (store, &iter,
            LIST_COL_NAME, col_values[0],
            LIST_COL_AGE, col_values[1],
            -1);

    /* Auto-adjust columns to fit data. */
    gtk_tree_view_columns_autosize (view);

    /* Return a zero to let SQLite know it's okay to continue processing */
    return 0;
}

/* User clicked the "Load List" button. */
static void
button_load_clicked( gpointer data )
{
    gint rc;
    gchar *err_msg = 0;

    /* Populate the list component with data from the DB. */
    rc = sqlite3_exec (db, DB_QUERY, process_db_results, data, &err_msg);
    if (rc != SQLITE_OK)
      {
        fprintf (stderr, "SQL error: %s\n", err_msg);
        sqlite3_free (err_msg);
      }

    return;
}

/* User clicked the "Clear List" button. */
static void
button_clear_clicked( gpointer data )
{
    GtkTreeModel  *model; 

    /* Get reference to underlying GtkTreeModel so we can clear it. */
    model = gtk_tree_view_get_model (GTK_TREE_VIEW (data));

    /* Clear the list using gtk_list_store_clear. This is much faster than
     * calling gtk_list_store_remove once for each row. */
    gtk_list_store_clear( GTK_LIST_STORE (model));

    return;
}

/* This callback quits the program */
static gboolean
delete_event( GtkWidget *widget,
              GdkEvent  *event,
              gpointer   data )
{
    /* Close the database connection. */
    sqlite3_close (db);

    /* Terminate the main program loop. */
    gtk_main_quit ();

    return FALSE;
}

/* Creates a list view component consisting of two columns. The first column
 * is for a persons name, the second for their age. */
static GtkWidget *
create_list_view( void )
{
    GtkCellRenderer        *renderer;
    GtkTreeViewColumn	 *column;
    GtkListStore             *store;
    GtkTreeView             *view;

    view = GTK_TREE_VIEW (gtk_tree_view_new ());

    /* --- Column #1 --- */
    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
            "text", LIST_COL_NAME,
			NULL);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_insert_column (view, column, -1);

    /* --- Column #2 --- */
    renderer = gtk_cell_renderer_text_new ();
    column = gtk_tree_view_column_new_with_attributes ("Age", renderer,
            "text", LIST_COL_AGE,
            NULL);
    gtk_tree_view_insert_column (view, column, -1);

    store = gtk_list_store_new (LIST_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
    gtk_tree_view_set_model (view, GTK_TREE_MODEL (store));

    return GTK_WIDGET (view);
}

/* Initialises the application's database connection. */
static void
init_db( void )
{
    gint rc;

    /* Open the database to initialise the connection object. */
    rc = sqlite3_open (DB_FILENAME, &db);
    if (rc)
      {
        fprintf (stderr, "Can't open database: %s\n", sqlite3_errmsg (db));
        sqlite3_close (db);
        exit(1);
      }
}

/* Initialises the application's user interface. */
static void
init_ui( void )
{
    GtkWidget *window;
    GtkWidget *vbox, *hbox;
    GtkWidget *scrolled_window;
    GtkWidget *list_view;
    GtkWidget *button_load, *button_clear;

    /* Create the main application window */
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_widget_set_size_request (GTK_WIDGET (window), 300, 150);
    gtk_window_set_title (GTK_WINDOW (window), "GTK/SQLite DB Example");

    /* Configure app to terminate when closed */
    g_signal_connect (G_OBJECT (window), "delete_event",
			G_CALLBACK (delete_event), NULL);

    vbox = gtk_vbox_new (FALSE, 5);
    gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
    gtk_container_add (GTK_CONTAINER (window), vbox);
    gtk_widget_show (vbox);

    /* Create a scrolled window to pack the CList widget into */
    scrolled_window = gtk_scrolled_window_new (NULL, NULL);
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
    		GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);

    gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
    gtk_widget_show (scrolled_window);

    /* Create the list view which will display the data. */
    list_view = create_list_view ();

    /* Add the list view widget to the vertical box and show it. */
    gtk_container_add (GTK_CONTAINER (scrolled_window), list_view);
    gtk_widget_show (list_view);

    /* Create a horizontal box to hold our buttons */
    hbox = gtk_hbox_new (FALSE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
    gtk_widget_show (hbox);

    /* Create the buttons and add them to the window. */
    button_load = gtk_button_new_with_label ("Load List");
    button_clear = gtk_button_new_with_label ("Clear List");

    gtk_box_pack_start (GTK_BOX (hbox), button_load, TRUE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (hbox), button_clear, TRUE, TRUE, 0);

    /* Connect our callbacks to the three buttons */
    g_signal_connect_swapped (G_OBJECT (button_load), "clicked",
    		G_CALLBACK (button_load_clicked), list_view);
    g_signal_connect_swapped (G_OBJECT (button_clear), "clicked",
            G_CALLBACK (button_clear_clicked), list_view);

    gtk_widget_show (button_load);
    gtk_widget_show (button_clear);

	/* The interface is completely set up so we show the window. */
    gtk_widget_show (window);

    return;
}

int
main( gint   argc,
      gchar *argv[] )
{
    /* Before we even think about doing anything we must initialise GTK */
    gtk_init (&argc, &argv);

    /* Initialise the database connection */
    init_db ();

    /* Initialise the user interface */
    init_ui ();

    /* Now that everything's ready, start the main program loop. */
    gtk_main ();

    return 0;
}

Not bad for 236 lines of code huh? Feel free to use the code in any way you like. You’ll have to create a new database using the sqlite command-line utility first before running it though:

$ sqlite3 temp.db
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> create table person (name varchar(30), age smallint);
sqlite> insert into person values ("Phillip J. Fry", 33);
sqlite> insert into person values ("Hubert J. Farnsworth", 167);
sqlite> insert into person values ("Zapp Brannigan", 45);
then type CTRL-D to exit

As far as I’m concerned writing this thing in C has a few compelling advantages:

  1. It’s portable. C is arguably the most portable (not to mention widely used) language in the world. If binaries aren’t available for your processor architecture, you can always compile it especially to suit that Z80 you’ve had sitting in your basement – just because you can.
  2. No special libraries or runtimes required here. Just run it baby.
  3. Glib, GTK+ again provide 95% of what I need for the app out of the box. Not to mention that the documentation is awesome and free. They’re cross-platform too!
  4. Working with relational databases is a snack. No OO/RDBMS disparity here, it’s all smooth sailing.
  5. It’s really, really fast. There’s no VM here, just bare-metal machine code.
  6. Like Java, there’s heaps of free libraries and tools out there to take advantage of.

Of course using something as raw as C/GTK+ isn’t without its hangups:

  1. Memory management and pointers frighten the bejesus out of many programmers. I don’t mind them so much, but I do understand that some have a pathological aversion to them.
  2. Cross-compilation isn’t exactly what I’d call straight forward.
  3. If you’ve been spoilt, like I have, working in 4th gen languages with awesome support for strings  NULL terminated character arrays are a rude shock. Don’t get me wrong, it’s not difficult, it’s just one extra thing to keep in mind.
  4. Very little in the way of syntactic sugar.
  5. GTK is still yet to be ported to Mac OSX. This is potentially a dealbreaker.

One thing in particular that I found was very nice was the GTK+ “Object Oriented” style interfaces which allow for a form of inheritance within a standard C program. This is a massive benefit because UI programming can get quite repetitive if you’re unable to extend and modify components simply by subclassing them.

Well that’s about all I’ve got for now. I’d be really interested in hearing what you think on the matter of technology choice, especially if you’re looking to contribute in future.

Follow

Get every new post delivered to your Inbox.