Showing posts with label software-design. Show all posts
Showing posts with label software-design. Show all posts

Tuesday, December 18, 2012

Code Smells: Calling Life-Cycle Methods of Program Components Explicitly

Here is some code I see that sets off alarm bells clanging in my head:

Explicitly calling life-cycle methods of components that are outside your control.

Since that sounds oh-so-generic, let me illustrate by way of example.

Assume you are writing a Servlet and your implementation doesn't care whether the request was a POST or a GET (in the current RESTful world, this should never be the case, but let's keep that aside for the purposes of this post). So, the following would appear to be a reasonable implementation:

public class MyServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        //Handle the request 
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        doGet(req, resp); //Just call doGet
    }
}

I would never do this though. This is because doGet, doPost and the other doXXX methods are life-cycle methods of the Servlet. I have no control over when and how the servlet engine calls them. I also have no control over what the engine does once these methods return. It is possible that the engine performs some house-keeping tasks once a doGet returns, and that action could be different from what is done once a doPost returns.

It is really a simple matter of extracting the "common functionality" between such life cycle methods into a method and calling that method.

public class MyServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        handleRequest(req, resp);
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
        handleRequest(req, resp);
    }

    /**
     * Method that extracts out the common functionality between GET and POST requests
     */
    private void handleRequest(HttpServletRequest req, HttpServletResponse resp){
        //Handle the request
    }
}

What if the life cycle methods are in an Interface?

One opinion that came up in a discussion was that it is safe to call other life-cycle methods if they are in an interface as opposed to a class. Note that the above code sample extends javax.servlet.http.HttpServlet which is an abstract class as opposed to the base javax.servlet.Servlet which is an interface. This has come up specially while discussing callbacks.

I disagree with this opinion since it assumes that any book-keeping has to be performed by the same (possibly abstract) class that denotes the life-cycle component. This is not true. Take an imaginary UI toolkit for example, which has a Pane as a UI element. Now, assume an interface for handling interaction callbacks:

interface InteractionListener{
    public void onClick();
    public void onRightClick();
    public void onLongClick();
    public void onDoubleClick();
}

In this case again, assume that you want the same action to be performed on long-click and right-click. So, you might be tempted to do this:

Pane pane = // ...

pane.setInteractionListener(new InteractionListener(){
        public void onClick(){
            //Handle single-click
        }

        public void onRightClick(){
            //Handle right-click
        }

        public void onLongClick(){
            onRightClick();
        }

        public void onDoubleClick(){
            //Handle double-click
        }
});

This is still wrong. Yes- InteractionListener is an interface and there is no way it can have a concrete method that might perform house-keeping tasks. However, you have no control over how the UI toolkit engine invokes the call-backs. You also have no control over what action the engine takes after your onXXXClick() methods return. The solution, again is exactly the same as before: extract the common code into a method which you then invoke from both onLongClick() and onRightClick().

Conclusion

Avoid calling one component life-cycle method from another, when the component in question is outside your control. This could lead to unpredictable behavior. Simply refactor the common behavior into a method and call that method from both life-cycle call-backs.

A few other instances where I have seen this sort of code:

  • Android's SqliteOpenHelper. In the onUpgrade() method, you probably want to take a backup of data, drop the tables, add new columns, and then re-create the tables. The implementation often looks like this:

    public class MySqliteOpenHelper extends SqliteOpenHelper{
        public void onCreate(SQLiteDatabase db){
            //Use SQL CREATE TABLE statements to create the tables.
        }
    
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
            /*
             * 1. Take backup of data
             * 2. Drop tables
             * 3. Add columns
             *
             * And finally:
             */
            onCreate(db); //This is not right.
        }
    }
    

    The right way to do this would be:

    public class CorrectSqliteOpenHelper extends SqliteOpenHelper{
        public void onCreate(SQLiteDatabase db){
            createDatabase(db);
        }
    
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
            /*
             * 1. Take backup of data
             * 2. Drop tables
             * 3. Add columns
             *
             * And finally:
             */
            createDatabase(db);
        }
    
        private void createDatabase(SQLiteDatabase db){
             //Use SQL CREATE TABLE statements to create the tables.
        }
    }
    

I will re-visit this post and add more instances as and when I come across them.

Wednesday, June 13, 2012

Internationalization in Spirit: Part 3 - Developers

This is the third post in a series about the idea behind making software truly global; and why we are not quite there yet. You can read Part 1 and Part 2 to get some context.

It goes without saying that the core of the software world is the development community. The success of any software platform (that could mean programming language/ API/ development stack/ tool/ … ) depends on how well developers take to it. Every platform out there has gone all out trying to woo not only users, but also developers. There’s documentation, end-to-end samples, fancy tooling, developer fora, mailing-lists, HowTo screencasts, support groups and what not. These days we even have video conferencing wherein the core developers of the platforms interact directly with the community.

The question, then is, is this sufficient to make the platform easier to consume for developers all over the world? From my experience, I would say that there is still lot of scope in this area.

To begin with, there seems to be an unwritten assumption that command over the English language is necessary in order to be a good programmer. This is reflected in

  • the complicated language in use in developer documentation
  • the “heavy” words used to name program elements (classes, functions, variables .. )
  • names of frameworks/API’s or the concepts (like design patterns) on which they are based. These are often cool or catchy, but again, they make sense to only a subset of the developers that use them.

I argue that this assumption is faulty. It is worth remembering that software development (particularly the services industry) is now big in BRICS countries and also picking up in countries like Argentina, Vietnam, Philippines. Most programmers might have basic English knowledge, but I seriously doubt their having a command over the language.

Developer Documentation:

I believe that currently, there is this thing about making documentation grammatically (and I don't know .. legally, politically, ...?) correct. This comes at the cost of clarity. While a developer who is sufficiently well-versed in English might easily understand the essence of the documentation, it is often confusing for developers who are not so well-versed in English.

Consider the following method doc:

String bestMatch(String text)

Scans the database of known keywords until a match on the supplied text occurs, unless said database is empty, in which case falls back to performing keyword search on description and content fields; in either case returning a best match if found, null otherwise.

Yes that was correct. But how easy was it to comprehend? Now, consider the same method doc re-written as follows:

String bestMatch(String text)

  1. Scans database of known keywords , attempting to match the supplied text with the keyword.

  2. If the database is empty, attempts a match on content and description fields.

In both cases, this method returns the best match String if found, null otherwise.

My point is, developer documentation should strive to be simple in language. It is always possible to re-word complex sentences by breaking them up into bullet points and the like.

Documentation should avoid using subtle grammar or obscure language constructs where possible. Needless "smart" wordplay is a strict no-no. Documentation is not a place to show off one's language skills, there are other forums well-suited for that!

Samples:

Back in college, we mostly used Animal, Cat and Dog to describe inheritance; and Student or Account as examples to illustrate data storage concepts. Somewhere down the line, example code went through a transition. Now, sample code looks like this:

Band ledzep = new Band();
ledzep.type = BandType.ROCK;
//...

performances.add(ledZep);

Or, as one of my favorite books used to describe singleton pattern using enums in Java: Elvis.INSTANCE.leaveTheBuilding();

Now that was cool ... for someone who has heard of Elvis. But, this may sound impossible, but there are developers who have never heard of Elvis. They will miss the point, for sure.

What I'm trying to say here is: Sample code should try to put across the point without being gimmicky. The gimmicks might be picked up by some, but will definitely be lost on some others.

Program Elements:

So, you are designing an API. You try to follow all the best practices. You start thinking of a name for that particular class. You want the class name to be as descriptive as possible. Then you end up with this:

public abstract class ImmutableMediationDelegatingStrategyPrefabricator

or a method like this:

public void incrementalAddThenComputeAverageAndPersist()

A potential developer takes one look at your API, and is likely to never return to your site again.

Okay, those were extreme examples. But not all that distant from the truth. The gist here is that the name of your API should help the developer understand its concise objective. If the developer has to reach for the dictionary to understand what the hell the class name means, you've got it wrong.

Libraries:

Ditto with the name of the library itself, where we see the other extreme. Its good to be creative and think of cool names; but there are plenty of examples of devs who overdid it, and ended up with names that make sense only to a small percentage of the target audience. This holds even if the catchy name is somehow related to the functionality the library provides (which is in itself a rarity).

Would you make an effort to understand my library if I named it kumbh-ka-mela? Or Kodachadri?

The Bottom Line:

It is simple really. As a developer, every aspect of your product is carefully designed keeping the end user in mind. The same consideration should be extended to other developers who will be using your development tool. Right from program concepts to developer documentation, one must strive to make the artifacts as inclusive as possible.

After all, more the developers use my development tool, the more I benefit. Right?

Tuesday, March 27, 2012

Internationalization, In Spirit - Part 2: Cultural Sensitivity

In Part 1 of this series, I touched upon how software should target a wider audience by being easy to consume by people around the planet. I used the term "internationalization" in that post, but I really meant a combination of internationalization (i18n) and localization (l10n). In this post, and the rest of this series, I use these terms interchangeably. I realize technically speaking they are different but I really think both these terms should for the most part, be mentioned in the same breath.


In my opinion, the biggest missing piece in the whole global software story is cultural sensitivity. When designing, developing and distributing software, we often overlook the fact that the user of this software could be from a culture that is diametrically opposite to our own. Let me refrain from taking names; but for a moment take a look at the list of software installed on your computer or phone and you will know what I mean. How many of the concepts presented in these software are you really familiar with?

I will draw up a list of hypothetical examples here to drive home my point:

  • Imagine a software tool that has one particular “fun” feature and names it Aloha. Now an American might immediately associate Aloha with Hawaii and therefore with fun, but what difference does it make to a person on the other side of the globe?
  • Imagine a role playing game in which one particular level involves the player attending high school prom. If your game targets a global audience, chances are most of your users have never been to prom; and those who do not have Hollywood influence don’t even know what a prom is.
  • Suppose you have a game where you grade your players according to high scores. What do you think would be the consequence of grading them as “Sachin”, “Viru”, and “Dhoni”? I’m sure people from non-cricketing nations reading this post won’t even get the reference.

One can go on and on pulling out examples, but you see where this is heading, don’t you? The underline here is this: In an attempt to make a product more user-friendly, we tend to link it to things that we see and use every day. Unfortunately, we fail to realize that those very “things” might be totally unheard of in markets where our products are targeted.


I don’t believe there is an easy solution though. None of the options seem to be good in their own right.

  • It is highly improbable that one will find one concept that fits all cultures.
  • The costs associated with adapting the very concepts the product is based on, to various cultures, is sure be prohibitive.
  • Keeping things culture-neutral is likely to make the product dry and boring - a kill-joy for sure.

In the end it is a matter of finding the balance that works for you. But before arriving at that formula, it is worth considering the cultural aspect in some detail so as to be in the good books of all end-users!


In the next installment of this series, we will look at how the software development community could be more inclusive of developers around the world.

Saturday, March 10, 2012

Internationalization, In Spirit - Part 1



You’re watching an amazing program on Discovery. It is about some mind-boggling natural wonder - say, the Grand Canyon, or the Amazon .. or something of the sort. You watch (and hear) in awe, as the narrator unfolds the statistics of the phenomenon. And then, in a bid to impress upon you how big the structure really is, he says “It is [insert-some-number] feet long, that’s [insert-some-other-number] New York city blocks put together”.

And you go, “How in the world am I supposed to know how big a block is?”. You feel anger and disappointment rising up in equal amounts. You start trying to figure out that calculation. You miss the next five minutes of the program. Before you know it, you’ve lost track, and interest.

****************

Rewind to networking class in your college days. You are studying the token ring algorithm. Your book explains the workings of the algorithm in detail, but every so often makes a reference to “token system in public service office counters”. Basically the book assumes that you are already amply familiar with this token-something-something system, whatever it is. Forget the book, the algorithm itself makes this assumption.

Bad assumption, you say. You come from a country where there is hardly a concept of a queue, let alone tokens. You have a hard time grasping the concept. Worse, you begin hating that algorithm!

****************

Okay. I admit that was exaggerated. But the fact remains.

Internationalization has gone from being a best practice to becoming an absolute requirement in most software applications. How widely a software is adopted is directly tied to how usable it is by people all over the planet.

These days it is pretty standard for any application development framework (language/platform/stack) to provide techniques for developers to easily internationalize their applications. Display messages, units, currencies are all externalized from the code itself. Heck, developers don’t even write the display messages - that task is outsourced to translators.

But, is this sufficient? Would people the world over be happy if their web app displayed messages in their language, Rupees instead of Dollars, and kilometres in place of miles? I think not. In this series of posts, I will present my argument on why I think the software world is still a far cry from being truly global.


Stay tuned for Part 2 of this series, which will talk about cultural sensitivity.