Recently Iāve been working on some highly dynamic User Interfaces and at one point in the project I found my first reflex on a certain task to use a switch statement in JavaScript.
Now, my previous training in studying the Design Patterns has told me that the switch statement is bad. The design pattern drilled into me to resolve the switch statement is the the Strategy Pattern, however, Iāve never used the Strategy Pattern in JavaScript before.
The following example is a simple demo application where I start with a switch statement and then refactor the code later in the blog post to use the Strategy Pattern.
A Simple Super Hero Demo Application
The sample application is a Super Hero creator. You provide a Super Hero name and then you select your Super Hero power. The power can be a variety of things ranging from Flying, Invisibility, to nothing at all. Some of the powers have additional questions (metadata) about that particular power.
Once the Super Hero is created, the application should build up a object with the appropriate power type and supporting metadata. For now, Iām just outputting the object in the Firebug Lite console using console.dir().
First Pass at a Switch Statement Solution
Here is the above application written using a switch statement to build up the Power type and metadata. The solution is fairly simple in nature. I am utilizing the Revealing Module Pattern.
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
The switch solution violates the Open/Closed Principle in that every time a new Super Hero power is added to the list that the same piece of code will need to be modified. This is problematic in that new bugs can easily be introduced as new features are added or as defects are resolved.
Letās look at a better way to solve the solution.
Refactoring Using the Strategy Pattern
A lof of the code will remain the same in the refactored solution. We mainly want to pull out the switch statement into something that is more maintainable and less error prone for future enhancment.
var selectedPower = $("input:radio[name='Power']:checked").val(); var scrapePowerFunction = "public.scrapePower" + selectedPower; power = eval(scrapePowerFunction)();
return power; };
public.scrapePowerFlying = function() { var power = {};
Abides by the Open/Closed Principle by opening the code for extension, but closing it for modification.
Cons of this Solution
The solution is more complex than the previous switch statement implementation
You might have noticed the use of the eval function. Douglas Crockford labeled the eval function as a Bad Part of JavaScript for some very good reasons, however, in this case I think the benefits it brings in this situation outweighs the risks. With every hard and fast rule, there comes a place and time to examine the benefits and risks.
Conclusion
I found that separating the switch statement logic into the above Strategy Pattern has made my current project much more maintainable and less error prone when adding new features.
How have you addressed similar situation? Have you implemented a different solution to the switch statement problem? Please share with me, Iād love to know!
I updated the Couch Potato bookmarklet to add the ability to expand and collapse the details of each document from the list page by clicking the ā+ā to the left of the document key.
You can also click the āExpand All Documentsā action located in the right navigation column.
The Bookmarklet
To use the bookmarklet, drag the following link to your bookmark/favorites list:
Weāve been using CouchDB in our current project at work and Iāve been using the CouchDB Futon manager more and more lately to create, edit, or delete documents.
After a lot of test data gets into our document store, I end up needing to clear out the documents. I could Delete the whole database with a button, but Iād rather not remove the CouchDB design documents defining custom views.
In order to delete documents from Futon, you have to manually drill down into each document and then click the delete button. I found this very monotonous, so I decided to make a bookmarklet to help me out some.
The Solution
So, I created the CouchDB Potato Bookmarklet (because Iām lazy). The bookmarklet creates a new delete column and provides a āDelete Documentsā link to delete all the checked documents. I also added a āSelect All Documentsā which only selects non-design documents (so that I donāt accidentally delete a CouchDB view). These links can be found in the right navigation column under the āRecent Databasesā section.
The Bookmarklet
To use the bookmarklet, drag the following link to your bookmark/favorites list:
If you have used ASP.NET MVC any, then you are probably aware of the MVC Contrib project hosted on CodePlex. It is a helpful library that provides useful tools that ASP.NET MVC doesnāt give you out of the box.
Opinionated Input Builders
One of the pieces that I really like is the Opinionated Input Builders that Eric Hexter wrote. The builder allows you to provide one property at a time from your View Model and it will output the label, required indicator, the appropriate input control, and whatever else that it needs to display.
As it turns out, the ASP.NET MVC team took a similar approach when putting together the Template Helpers inĀ ASP.NET MVC 2. I ended up switching to the Template Helpers.
<% Html.EnableClientValidation(); %> <% using (Html.BeginForm()) { %>
<%= Html.EditorForModel() %>
<% } %>
With some CSS styling, the output of the EditorForModel is close to what the Opinionated Input Builder, but there are some problems as weāll discuss in the next section.
ASP.NET MVC 2 EditorFor
The problem is that I usually donāt want to display or edit my entire model. I often times have things in my View Model that I donāt particularly want to display.Ā I want to rather manually choose which properties I want to display or edit.
<% Html.EnableClientValidation(); %> <% using (Html.BeginForm()) { %>
<%= Html.EditorFor(m => m.FirstName) %> <%= Html.EditorFor(m => m.LastName)%> <%-- Purposely Not Show the Email ā%>
<% } %>
Using the above syntax doesnāt quite give the output I was looking for. Only the input controls are rendered (as seen in the following screenshot) instead of providing the scaffolding of label, input, and validation fields like the EditorForModel method provides.
You end up having to provides the layout of the controls and explicitly mention the label, input controls, and validation for each property.Ā
Modify the Template Helpers MasterPage
I soon began to miss the simple syntax of rendering all the necessary code like I was used to when using the Opinionated Input Builders outputted from the MVC Contrib.
Well, a while back Brad Wilson (one of the ASP.NET MVC 2 developers), wrote an awesome series about Templated Helpers and the last post in the series, ASP.NET MVC 2 Templates, Part 5: Master Page Templates, he addressed this granular Opinionated Input Builder type syntax!
All you do, is to override the Master Page that the templates use internally. So, inside my Master Page I layout where I want the label, validation, and input controls to go and then ASP.NET MVC 2 does the rest by resolving which Template Helper to use!
I modified the Master Page some to suite my needs (I use divs instead of tables), but overall it is the same one that he lays out on his blog.
There are several other files that youāll need. Iāve grabbed most of them from Bradās blog post and have them in the demo application below that you can download.
Now we can try the above scenario one more time and get the output that we were expecting.
<% Html.EnableClientValidation(); %> <% using (Html.BeginForm()) { %>
<%= Html.EditorFor(m => m.FirstName) %> <%= Html.EditorFor(m => m.LastName)%> <%-- Purposely Not Show the Email ā%>