Caffeinated Simpleton

Archive for December, 2008

A better object oriented JavaScript

There are some problems with objects in JavaScript. The system is beautiful in its simplicity, but at times the JavaScript approach to objects is limiting and confusing. I have found this to be especially true when trying to develop large pieces of software with many components written by different people that need to interact.

To deal with these situations, I have set out to provide an object system that has a consistent syntax across a variety of situations, can be introspected with the console as much as possible, and exploits the strengths of the JavaScript object system. This isn’t easy, but by starting with a solid foundation and adding a few key features, I feel that I’m well on my way.

First, let me illustrate some specific issues that I come across in day to day development.

The first is static classes. These are typically manager type classes that regulate access to resources. Connection managers are a good example. An important attribute for these classes is that they cannot be re-instantiated. They are singletons. One approach to these is the namespace:

ConnectionManager = {
     connect: function(data) {
         do_stuff_with_data(data);
     },
     disconnect: function() {
        close_my_connection();
     }
}

This does not leave behind a constructor, so it is not possible to accidentally create another ConnectionManager object. However, there is no constructor at all. If I need to do some initialization of this object, I can do the following:

ConnectionManager = new (function() {
     var my = this;
     var my_private_data = initialize_something();
     my.connect = function(data) {
          do_something_with_data(data, my_private_data);
     }
 
     my.disconnect = function(data) {
         close_my_connection();
     }
})();

This form has a couple of advantages. First, you can initialize things. This often has to happen, so it already has an advantage over the first form. Secondly, you can control your scope. In the preceding example, I assign this to my. From any of the member functions, I am then free to use my in place of this with the knowledge that it will be the correct object. I never have to worry about binding. Finally, you can hide private data. By keeping private data in an enclosing scope, you keep it from being used improperly. This isn’t to say that I think private variables are a necessary part of an OO system, but when I’m designing an interface, it’s a bit more pleasant when the person I’m designing the interface for can inspect my object without wading through a bunch of implementation details.

Beyond the Singleton case, there are more traditional object oriented issues. Without any help from a library, a typical class definition would looks something like this:

Connection = function(){
     initialize_some_stuff();
};
 
Connection.prototype.permissionMap = {
     1: 'read',
     2: 'read-write',
     3: 'none'
};
 
Connection.prototype.open = function() {
     open_this_connection();
};
 
Connection.prototype.close = function() {
     close_this_connection();
};

This is a typical JavaScript class that uses prototypes. Prototypes are a necessary part of defining classes in JavaScript since a prototype defines all the properties that will be defined across all instances of a class. This is essentially how most people expect classes to work (coming from a more traditional OO point of view).

Also included in this example is a static member. The permissionMap above is an example of providing static data across all instances of a class. This is a good thing to do for constants, jump tables, string tables, or anything else that doesn’t change but might take up a lot of memory. Data that does change on an instance by instance basis cannot be defined as a member of the prototype or when it is changed by one instance, that change will be reflected across all instances. That data should be established and initialized in the constructor (The Connection function above).

There are some problems with this syntax. The first is that it’s not obvious what’s going on. People who use prototypes all the time know what’s going on, but it is never explicitly stated. For instance, permissionMap is a static variable. I’ve explained why, but nowhere is that clear in the code. This form is also quite verbose. I get so tired of typing Connection.prototype that I usually just making a shortcut variable and deleting it after the method definitions are all over with. Finally, this syntax doesn’t really look like any of the other objects we defined. It looks like a bunch of functions with the same prefix. Nowhere is it implied that this is a class.

Out of those basic problems, I have started trying to define a common syntax that can fit any of the situations above, plus basic inheritance (which is basically just copying and extending a prototype). In addition, I’m building in as many introspection features as possible to make interfaces defined in this way a snap to sit down and figure out how to use.

I haven’t made that much progress yet, but I was given a big head start by basing all of this off of the outstanding mootools Class system. I really enjoy their code, their approach to modifying JavaScript, and their minimalism. Mootools’ system, in turn, is based on Dean Edwards’ Base.js. As you can see, I have a nice body of work backing me up here. It should make my job much easier.

I’ll go into more detail about how I want to solve these problems in a later post. For now, you can follow development on my fork of mootools-core at github.

Testing for fun and profit

First of all, let me be the first to admit that I don’t have it all figured out when it comes to how to design software. I have, however, put a lot of thought into how to properly test software.

There is a bit of feud these days about whether testing is the end-all be-all of software development (Test Driven Development), or a complete waste of time (which Knuth famously said a few months ago). I don’t go to either extreme, and I can see both sides.

Test driven development has an advantage in that you have maximum test coverage, which can give you maximum peace of mind. Also, since the tests are written before the actual code, the code is necessarily testable. Testable code tends to be very encapsulated and overall better designed.

I will concede that the quality of code generated under TDD is probably the highest. However, programming is not as fun when you have to write tests for every single condition. I program because it’s fun. I like seeing things start working. I like playing with how things work before settling on an implementation. I don’t want to stop to write a new test before every single next step. That just kills my momentum.

One alternative to TDD is to not have unit tests at all. Instead, have general regression tests and have people pound through it. In my experience, this does not work. I’ve never written a piece of code that I wouldn’t have written better on a second pass, so in my mind the ability to rework code is absolutely vital to developing good software. Unit testing aides refactoring code tremendously.

Those two ideas come together for me in a way that keeps programming fun. I experiment for a while and get a working implementation that I’m happy with. Then I write the tests that cover the basic functionality, but not necessarily every edge case (though I will add those if they come up while debugging). Writing these tests is fun, because I can see how much the code improves when I rework it be testable, at least from the external interfaces. Then, weeks later when I realize the code needs to be extended in a way I had not anticipated, I can rework it and be assured that I didn’t miss anything stupidly obvious (IE. I don’t “break the build”). This is especially important in scripting languages where compilers won’t catch things like a misnamed variable or a misapplied rename.

The point of this is, testing isn’t a religion. It’s a means to an end. The end is good software, and I think a well balanced approach does pretty well.