"Clean Code" in AS3
In 2008, Uncle Bob (Robert C. Martin) proposed to the Agile Community that the Agile Manifesto should have a fifth tenet: “Craftsmanship over Execution”.
It is often held as obviously true that functionality is the most important element of code, since without functionality code is useless. So long as code works, readability and execution speed are valuable. Broadly, Martin argued that this is wrong: craftsmanship is more important than functionality; the professionalism with which we produce our code is the most valuable aspect of our work.
Most software development teams execute, but they don’t take care. We value execution, but we value craftsmanship more. Uncle Bob
It is clear to see why this is a difficult sell! At the moment I work at an agency, where in many cases the typical ‘agency mentality’ prevails: work is to be done as quickly as possible. However, all experienced coders know the consequences of trying to do too much too quickly: quick work leads to bugs, bugs lead to more work, dissatisfied clients, frustrated bosses, and – too intangibly to explain to most bosses – more work on future projects because of less code-reuse. Perhaps you know and accept all this, but it is worth repeating. And repeating, and repeating.
That said, too many of the Flash community seem not to know these truths. The Flash community is in great need of learning from the Agile elder statesmen. Most third party libraries value functionality above all and show little care for craftsmanship.
The history of Flash coding has been to strive for speed; to squeeze functionality out of the Flash Player like no-one had squeezed before. Many people have also created third-party libraries to help fellow coders shortcut to functionality and avoid reinventing a particular wheel. Unfortunately those libraries tend to be poorly written, buggy and unreadable. Even classes in the AS3 Core Library, which is run by some significant Flash notables, seem to have been rushed, no effort taken to clean them up for future developers to learn from or extend.
A Practical Example – The Delaunay Algorithm
Recently I wanted to do some work by creating a Delaunay Triangulation Diagram. I knew what it is, and roughly how the algorithm worked, but as would most people, I Googled it in the hope that someone had already created the algorithm in AS3 that I could use. I found this post on indiemaps.com. It’s a lovely post if you want to understand what the Delaunay Triangulation is or what it does, but the accompanying code is utterly horrible. I defy anyone to be able to read through this algorithm and understand how it works. Here’s a snippet:
public static function triangulate(pxyz:Array):Array {
var v:Array=new Array();
var nv = pxyz.length;
for (i=0; i < (nv*3); i++) {
v[i]=new ITriangle();
}
// the points must be sorted on the x dimension for the rest to work
pxyz.sortOn("x", Array.NUMERIC);
var complete:Array = null;
var edges:Array = null;
var nedge = 0;
var trimax, emax = 200;
var status = 0;
var inside:Boolean;
var xp, yp, x1, y1, x2, y2, x3, y3, xc, yc, r;
var xmin, xmax, ymin, ymax, xmid, ymid;
var dx, dy, dmax;
var ntri = 0;
/* Allocate memory for the completeness list, flag for each triangle */
trimax = 4*nv;
complete = new Array();
for (var ic=0; ic<trimax; ic++) complete[ic] = false;
/* Allocate memory for the edge list */
edges = new Array();
for (var ie=0; ie<emax; ie++) edges[ie] = new IEdge();
/*
Find the maximum and minimum vertex bounds.
This is to allow calculation of the bounding triangle
*/
xmin = pxyz[0].x;
ymin = pxyz[0].y;
xmax = xmin;
ymax = ymin;
for (var i=1;i<nv;i++)
{
if (pxyz[i].x < xmin) xmin = pxyz[i].x;
if (pxyz[i].x > xmax) xmax = pxyz[i].x;
if (pxyz[i].y < ymin) ymin = pxyz[i].y;
if (pxyz[i].y > ymax) ymax = pxyz[i].y;
}
dx = xmax - xmin;
dy = ymax - ymin;
dmax = (dx > dy) ? dx : dy;
xmid = (xmax + xmin) / 2.0;
ymid = (ymax + ymin) / 2.0;
The problem with this class is plain: it was ported. Clearly it was also ported from a functional language that had no pretence towards OOP. Such ports rely on a line-by-line transposition from one language to another and a bit of testing to check they work. Porting normally means that the porter doesn’t understand what is being ported. It is translation but rarely transliteration.
In the case of the Delaunay Algorithm, perhaps this is not much of a problem; there aren’t really many ways to extend this algoritm. In the case of a framework however, it ought to be unforgivable. If people can’t read the code they can’t improve it, amend it, extend it, adjust it. It makes them consumers and prevents community interaction. The code, the users and the original developer all suffer because of it over the long term.
Faced with this code, I decided to write an implementation of Delaunay’s algorithm myself. I have had this reaction to nearly every piece of library code I’ve read recently! Out of frustration, I have started writing unit testing frameworks, bulk loaders, tween engines, architectural frameworks, 3D engines, and game engines before realising that I don’t have enough time to all these libraries’ functionalities, and will have to live precariously by accepting that the library is closed to me because of its practical complexity and unreadability.
I must point out that I greatly admire and respect the coders who have successfully created the versions of these frameworks that I use (and would admire a great many others if only I knew your work better!), but I can’t help be frustrated at the effort I have to expend in order to read what they have done, particularly if I find a bug! It is the wasted effort that frustrates me: someone understood what they were doing when they wrote this the first time, so why didn’t they write it in such a way that I could understand too?
My own version of the Delaunay Algorithm is in Google Code as the first iteration of the as3voronoi library. It is a work in progress and by no means perfect. Uncle Bob would chastise many of my naming choices, ridiculous comments, and probably some of the design choices that I have made. However, I think that you can at least read this implementation, starting with the math.delaunay.Delaunay class and working out into the other classes as necessary. Here is a method:
public function addPoint(point:Point):void
{
if (!listOfPoints.add(point))
return;
if (listOfPoints.count == 3)
addInitialTriangle();
else
addPointToTriangulation(point);
listOfEdges.clearUnusedEdges();
}
This is not a direct comparision between two functionally equivalent pieces of code, but merely try to give the flavour of the approach.
Please Write More Beautiful Code
If you can’t explain it simply, you don’t understand it well enough. Albert Einstein
If you are a serious AS3 coder, writing serious code that you hope people will take on and use for serious projects, then please write beautiful code, not just functional code. Write code that explains itself, that reads like English, that uses both sides of peoples’ brains.
Please don’t dazzle the community with archane cleverness, complex methods, and huge classes.
Please don’t let your code rot, by adding functional change on functional change without reflecting on the general structure of your code and refactoring where practical.
If you come from an artistic background and aren’t steady on your coding feet, innovate and explore and hack around, but then sit down with a friendly developer and pair program your innovation into something clean, readable, and reusable.
And please fight when your bosses or fellow coders contend that it’s not worth it. If it isn’t worth it on that day (and it probably will be), it certainly will be in three months time.
Agile Software Development Resources
These two books from Uncle Bob are excellent resources for learning about writing more elegant code:





Pingback: Voronoi Diagrams – as3voronoi v.0.2 at AlecMcE.com