You are currently browsing the Object Oriented Design archives.

Persisting Backbone Collections

Tuesday, April 10th, 2012

Backbone.js provides a light-weight, extensible API which focuses primarily on general structure, and the utility of that structure, without requiring a rigid adherence to specific patterns or prescribing certain design philosophies. This focused approach affords developers a significant level of flexibility which can prove to be essential to the success of many modern client side web applications – especially when considering the challenges surrounding the ever changing landscape of the web.

Naturally, it follows that such flexibility imparts additional responsibility on the developer. In my experience with architectural frameworks in general, and Backbone in particular, many times this kind of trade off can be quite preferable.

Extending Backbone

As mentioned above, Backbone implements the essential building blocks necessary to provide general structure within an application. If a feature is needed which is not provided by default, by design, Backbone allows for easily extending the core APIs in order to implement additional features as needed. In fact, developers are encouraged to do so.

Perhaps a good example of this can be found in the need to persist collections in Backbone; that is, interestingly, while the Backbone Model API implements a default save method, the Backbone Collection does not provide a similar API for explicitly saving the models within a collection; that is, saving the collection itself. This feature may have been left out intentionally as part of an initial design decision, as there are some complexities to saving an entire collection as a general use-case; such as saving deltas, propagating saved state to models within the collection and so forth; or, perhaps it simply may have been an oversite. Regardless of the reason, in certain scenarios it can be useful to save an entire collection rather than saving each model individually – fortunately, given the flexibility of Backbone, writing an extension to implement this feature is quite simple.

The PersistableCollection

As a basic solution for saving a Backbone Collection, I wrote a general abstraction – PersistableCollection, which extends Backbone.Collection, implementing a default save method.

The PersistableCollection API is quite simple in that it essentially just implements a proxy which wraps the collection in a Backbone Model, and ensures there is only one proxy created per collection. The url and toJSON methods, respectively, of the proxy Model reference the url and toJSON methods of the collection to which the proxy is bound. You can either extend PersistableCollection or generate a proxy for an existing collection via the static createProxy method. Examples of each follow.

Using a PersistableCollection

A PersistableCollection can be instantiated and used directly, just like a Backbone.Collection:

Extending PersistableCollection

You can extend PersistableCollection and simply invoke save to persist the models in the collection:

Creating a proxy for an existing collection

An existing collection can be saved by creating a proxy for it:

The PersistableCollection can be used as a general extension for saving entire collections based on the representation of all models within the collection. If you are interested in extending it to account for more specific use-cases, you can simply override the default save implementation as needed.

You can find the gist for the source and spec here.

Testing Handlebars Helpers with Jasmine

Tuesday, March 13th, 2012

For some time now, I have primarily been using logic-less templating solutions as they allow for a greater separation of concerns in comparison to many of their logic-based counterparts. By design, the decoupling of logic-less templates imparts greater overall maintainability in that templates become considerably less complex, and therefore, considerably easier to maintain and test.

Handlebars, my preferred logic-less templating engine, simplifies testing even further via it’s elegant Helper API. While Handlebars may not be the fastest templating solution available, I have found it to be the most testable, reusable and thus, maintainable.

Custom Handlebars Helpers

Since Handlebars is a logic-less templating engine, the interpolation of values which require logical operations and/or computed values is facilitated via Helpers. This design is quite nice in that template logic can be tested in isolation from the context in which it is used; i.e. the templates themselves. In addition to the common built-in Block Helpers, custom Helpers can easily be registering in order to encapsulate the logic used by your templates.

Registering Custom Helpers

Registering Custom Helpers is as simple as invoking Handlebars.registerHelper; passing the string name of the helper which is to be registered, followed by a callback which defines the actual helpers implementation.

Consider the following custom Helper example, which, given a string of text, replaces plain-text URLs with anchor tags:

(Gist)

As can be seen in the above example, custom Handlebars Helpers are registered outside the context of the templates in which they are used. This allows us to test our custom Helpers quite easily.

Testing Custom Helpers

Generally, I prefer to abstract testing custom Helpers specifically, and test the actual templates which use the Helpers independently from the Helpers. This allows for greater portability as it promotes reuse in that common custom Helpers (and their associated tests) can then be used across multiple projects, regardless of the templates which use them. While one can test Handlebars implementation code with any testing framework, in this example I will be using Jasmine.

Essentially, testing Custom Helpers is much the same as testing any other method. The only point to be noted is that we first need to reference the helper from the Handlebars.helpers namespace. Ideally this could be avoided as, should the namespace happen to change, so, too, will our tests need to change. That being said, the probability of such a change is unlikely.

Using the above example, in a Jasmine spec, the enhance helper can be referenced as follows:

Then we can test that the helper was registered:

We can then test any expectation. For example, the enhance helper should return a Handlebars.SafeString. We can test this as follows:

The enhance helper is expected to replace plain-text URLs with anchor tags. Before testing this, though, we should first test that it preserves existing markup. In order to test this use-case, we first need to access the return value from our custom Helper, we can do this by referencing the string property of the Handlebars.SafeString returned by our Helper:

Finally, we test that our enhance Helper replaces URLs with anchor tags using the above techniques:

(Gist)

We now have a complete test against our custom Helper, and all is green:
Custom Helper Spec
Note: The above Spec Runner is using the very nice jasmine.BootstrapReporter

And that’s all there is to it. You can fork the example at handlebars-helpers-jasmine.

Jasmine matchers for Sinon.JS

Wednesday, March 7th, 2012

Lately I have been finding myself writing custom Jasmine matchers which wrap the Sinon.JS API as a convenience. After repeating this process quite a few times I took a step back to see if there was a similar solution already available.

After a brief search, I quickly came across jasmine-sinon which, to my surprise, provides a very similar matcher API to that which I had began implementing. This library really is quite nice as it essentially provides matchers for all common Sinon.JS spies, stubs and mocking use-cases. I am sure jasmine-sinon has saved many developers a lot of time by not having to write their own custom matchers as it has for me.

And so, if you are using both Jasmine and Sinon.JS, and find yourself writing convenience matchers to simplify Sinon.JS integration, jasmine-sinon is an excellent addition which compliments both and is certainly worth considering.

Function Overwriting in JavaScript

Saturday, November 19th, 2011

Whether intentional, or simply a by-product of it’s design, Javascript, being a dynamic language, allows for a level of expressiveness which most any seasoned programmer would come to appreciate. Javascript naturally provides the ability to implement some rather intriguing and quite unique patterns; one of which is the ability to overwrite a function at runtime.

Function Overwriting

Function Overwriting (also known as “Self-Defining Functions” or “Lazy Defining Functions”) provides a pattern which, as stated above, allows for overwriting a function’s definition at runtime. This can be accomplished from outside of the function, but also from within the function’s implementation itself.

For example, on occasion a function may need to perform some initial piece of work, after which, all subsequent invocations would result in unnecessarily re-executing the initialization code. Typically, this issue is addressed by storing initialization flags or refactoring the initialization code to another function. While such a design solves this problem, it does result in unnecessary code which will need to be maintained. In JavaScript, perhaps a different approach is in order: we can simply redefine the function after the initialization work has been completed.

A possible candidate use-case for Function Overwriting is Feature Detection as, detecting for specific feature support in the Browser typically only needs to be tested once, at which point subsequent tests are unnecessary.

Below is a basic example of implementing Function Overwritting in the context of an abstraction of the HTML5 Geolocation API.

Considerations

Since functions are objects in Javascript, it is important to keep in mind that if you add a property or method to a function (either statically or via the function’s prototype), and then overwrite the function, you will have effectively removed those properties or methods as well. Also, if the function is referenced by another variable, or by a method of another object, the initially defined implementation will be preserved and the overwriting process will not occur. As such, be mindful when implementing this pattern. As a general rule of thumb, I typically only implement Function Overwriting when the function being redefined is in a private scope.

Concluding Thoughts

As you can see, Function Overwriting provides a convenient facility for optimizing code execution at runtime. There are many use-cases for dynamically overwriting functions and, where appropriate, they can certainly provide value in terms of performance and code maintainability.

Below you can find an example which demonstrates two basic Function Overwriting implementations. Simply load the page and add some breakpoints in Firebug to test the execution paths; both before and after overwriting each function occurs, or you can simply view the source.
Example

Circumventing Conditional Comparisons

Friday, October 14th, 2011

Often during the course of my day I come across code which evaluates the same conditional comparisons in multiple contexts. Understandably, this is rather typical of most software systems, and while it may only introduce a negligible amount of technical dept (in the form of redundancy) for smaller systems, that dept can grow considerably in more complex, large scale applications. From a design perspective, this issue is applicable to nearly every language.

For example, consider a simple Compass class which defines just one public property, “direction” and, four constants representing each cardinal direction: North, East, South and West, respectively. In JavaScript, this could be defined simply as follows:

Technically, there is nothing problematic with the above class signature; the defined constants certainly provide a much better design than conditional comparisons against literal strings throughout implementation code. That being said, this design does lead to redundancy as every instance of Compass which needs to evaluate the state of direction requires conditional comparisons.

For example, to test for Compass.North, typically, client code must be implemented as follows:

Likewise, simular comparisons would need to be implemented for each cardinal direction. And, while this may seem trivial for a class as simple as the Compass example, it does become a maintenance issue for more complex implementations.

With this in mind, we can simplify client code by defining each state as a specific method of Compass. In doing so, we afford our code the benefit of exercising (unit testing) Compass exclusively. This alone improves maintainability while also simplifying client code which depends on Compass. As such, Compass could be refactored to:

Based on the above implementation of Compass, the previous conditional comparison can be refactored as follows:

Comparator API

To simplify implementing conditional comparisons, I have provided a simple Comparator API that defines a single static method: Comparator.each, which allows for augmenting existing objects with comparison methods. Comparator.each can be invoked with three arguments as follows:

type

The Class to which the comparison methods are to be added.
property
The property against which the comparisons are to be made. If the property has not been defined it, too, will be added.
values
An Array of constants where each value will be used to create a new comparison method (prefixed with “is”). If the constants specified are Strings, typically an Array containing each constant should suffice. For example, passing [Foo.BAR] where BAR equals “Bar” would result in an isBar() method being created. To specify custom comparison method names, an Object of name/value pairs can be used where each name defines the name of the method added and the value is the constant evaluated by the method. This is useful for constants which are not strings. For example, {isIOS421: DeviceVersion.IOS_4_2_1} where IOS_4_2_1 equals 4.2.1 would result in an isIOS421() method being created.

Taking the Compass example, the previous comparison methods could be augmented without the need to explicitly define them via Comparator.each:

The above results in the comparison methods isNorth, isEast, isSouth and isWest being added to the Compass type.

Comparator: source | min | test (run)

Multiple Form Factor Software Design

Sunday, March 6th, 2011

I have been giving a lot of thought lately about designing software in a Multi-Form Factor paradigm and felt I would share some initial thoughts on the subject. Keep in mind much of this is still quite new and subject to change; however, I have made an attempt to isolate what I feel will remain constant moving forward.

First, User Experience Design

My initial thoughts on the implications of what an ever growing Multi-Form Factor paradigm will have on the way we think about the design of software are primarily concerned with User Experience Design. While using CSS3 media queries to facilitate dynamic layouts will be needed for most Web Applications, I do not believe these types of solutions alone will allow for the kinds of compelling experiences users have come to expect, especially as they will likely compare Mobile Web Application experiences to their native counterparts. Sure some basic solutions will be needed, and for some simple websites they may suffice. However, in the context Web Applications, as well as just about every application developed specifically for a PC, too, I believe UX Design will need to leverage the unique opportunities presented by each particular form factor, be it a PC, smartphone, tablet or TV. Likewise, UX will need to account for the constraints of each form-factor as well. Architecturally, all of the above presents both opportunity and challenge.

To further illustrate this point, consider the fact that it is arguably quite rare that a UX Design intended for users of a PC will easily translate directly to a Mobile or Tablet User Experience. The interactions of a traditional physical keyboard and mouse do not always equate to those of soft keys, virtual keyboards and touch gesture interactions. Moreover, the navigation and transitions between different views and even certain concepts and metaphors are completely different. In simplest terms; it’s not “Apples to Apples”, as the expression goes.

With this in mind, as always, UX Design will need to remain at the forefront of Software Design.

Second, Architecture

Multi-Form Factor design obviously poses some new Architectural challenges considering the growing number of form factors which will need to be taken into account. The good news is, most existing, well designed software architectures may have been designed with this in mind to a certain degree. That is, the key factor in managing this complexity I believe will be code reuse; specifically, generalization and abstraction. A common theme amongst many of my posts, code reuse has many obvious benefits, and in the context of Multi-Form Factor concerns it will allow for different device specific applications to leverage general, well defined and well tested APIs. A good example being a well designed RESTful JSON service.

Code reuse will certainly be of tremendous value when considering the complexities encountered with Multi-Form Factor design. Such shared libraries, APIs and Services can be reused across applications which are designed for particular Form-Factors or extended to provide screen / device specific implementations.

Some Concluding Thoughts

In short, I believe both users and developers alike will be best served by providing unique User Experiences for specific Form Factors as opposed to attempting to adapt the same application across Multiple Form Factors. One of the easiest ways of managing this complexity will inevitably be code reuse.

I also believe the main point of focus should be on the medium and small form factors; i.e. Tablets and Smart phones. Not only for the more common reasons but, also because I believe PCs and Laptops will eventually be used almost exclusively for developing the applications which run on the other form factors. In fact, I can say this from my own experiences already.

While there is still much to learn in the area of Multi-Form Factor Design, I feel the ideas I’ve expressed here will remain relevant. Over the course of the coming months I plan to dedicate much of my time towards further exploration of this topic and will certainly continue to share my findings.