Tuesday, September 18, 2012

Software imitates nature

In some software I participated, a web application is actually divided into two projects during development, and two deployable artifacts during runtime. This design addresses the idea that the UI of an application should only be the presentation of the application, while all the logic keeps the same in back-end even when front-end UI is changed. In this case, a lot of communication between front-end and back-end are required. And a lot of problems are also caused by this kind of communication. Let me give you an example.

There is a list of items in browser. User can make changes to the list, add items to the list or remove items from the list. Each time the list is changed, an asynchronized RESTful request is sent to back-end to update the model. Everything seems fine but sometimes it works strangely as the front-end and back-end have different items of the same list.

We finally figured it out that this is because the items in the list might change before the previous request is finished, and the change may or may not be sent to back-end and processed in order. Solving the problem is easy once we know the reason. Developers decided to temporarily disable the list from being modified until previous request is finished. Problem solved, well, for this specific front-end.

But if we turn to real life for such scenario, helmsman repeating verbal commands is one example to avoid misunderstanding between two parties. Back to the problem, to keep single source of truth, we can also return items in list as response and request any front-end to update the items in list to be the same.

You might wonder why bother doing the same thing twice. It's because they have different point of view. Disabling changes in front-end while waiting is a solution for a front-end not to have different state from back-end. Replying the state in back-end is telling any front-end what's in back-end is the only truth. Following the latter one is the key, while the former one is just a user-friendly add-on.

This is just an example that many software problems are really projections of real life problems that already got solved. Don't try to re-solve them when you can simply follow patterns that are already proved in nature.

Tuesday, August 28, 2012

A better way to solve Refused Bequest

There is a bad smell called Refused Bequest, which means subclasses don't want or need what they are given by superclass. According to Refactoring: Improving the Design of Existing Code (by Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts p.87), there are two ways to solve it. Traditional way is to create a new sibling class and use Push Down Method and Push Down Field to push all the unused methods to the sibling. Or use Replace Inheritance with Delegation, if the subclass does not want to support the interface of the superclass.

I have to say I'm shocked by how easily this expression could mislead those less experienced developers. How come bad smells in code can be solved like taking OTC medicines without even asking readers to focus on their software design.

Let me start with the 2nd solution. If there is no is-a relationship, there shouldn't be a class hierarchy. That said, removing a hierarchy should only be supported by the evidence that the original is-a relationship is not valid any more. By removing the hierarchy, two classes can only interact with each other in OO way - sending messages. This is the point. Readers shouldn't be misled that delegation can be used to remove inheritance.

The 1st solution has potential problems in any working system. Pushing method / field from parent to newly created sibling, just because a subclass in the inheritance tree doesn't want the method / field? This definitely breaks the contract between parent class and the rest of the world. A more engineering way is introducing a new base class, moving all the methods / fields the subclass wants from original base class to new base class and making both the subclass and the original base class extend new base class. Again, problem is solved by applying OO design, not by taking OTC medicines.



I'm not saying identifying bad smells and refactoring to solve them are of no values. I just want to highlight that bad smell comes from bad (or outdated) design, and can only be solved by good (or updated) design. Remember, refactoring with books at hand but nothing in mind is also a bad smell.