Code Refinement
Everyday is an opportunity to learn something new. Today, I learned how to set the tab width for vi (set tabstop=2) while trying to format the picture for this blog entry, for example. Daily learning is a form of personal refinement. Accumulated knowledge allows people to do things better.
If the lesson relates to development, then that piece of incremental personal development can be used to enhance software development.
Here are snippets of code that I have written and currently maintain:
The functionality of both versions are essentially identical. I wrote the version on the left side approximately two years ago. The one on the right side is the current and evolving version.
Changing the function’s accessibility is a significant evolution of the code. Straying from the simple public/private access control dichotomy, I employ protected accessibility to allow child classes to reuse this code. It is currently labeled final, because I do not want child classes to be able to override this function at this time. I may remove this restriction, if I later feel that there is a need. This follows the computing adages of minimizing interfaces, minimizing code duplication, and employing code reuse. Restrictions on a module’s interface can be relaxed. Restrictions cannot, however, be increased due to possible dependencies. Code may have been implemented to depend on a module’s interface, and to restrict accessibility to a function of the module, for example, would require code to be reworked. The cost of reworking the code that depends on the relaxed interface to use a more restrictive one grows significantly along with the amount of dependent code.
Supporting two versions of a particular class encouraged my use of inheritance here. Two files were used to provide a debug and a production version of the class. Modifying code that is common to both versions required that code be copied from one file to the other. The duplication of code is a “smell,” as Fowler puts it, that indicates the code can be better organized. Instead of replacing the debug and production versions of the files containing a class as I did earlier, which is a potentially dangerous practice, I use inheritance to localize duplicate code into the parent class and employ a factory method (not shown) that returns a debug or production instance of the class. If dependents on this class use the factory method to create an object, they do not have to be aware of which child class an object is. This assumes that the dependent code is written to depend on the interface that is defined by the factory method’s return type, which is usually a parent class. Since an object of the parent class can be substituted with objects of child classes, the factory method can return an instance of the child class while advertising the parent class as its return type to dependent code, and the dependent code will act on the object just the same.
The use of accessors to fetch the field values of a class instance is another significant change in the code. Although this does not serve more than little protection from unintentional instance variable modification (the instance variables could still be accessed directly in the member function), it may be useful if lazy evaluation is employed. So, the use of accessor functions is a good practice. A little indirection seems to always be good.
I also introduced a dependency into the function’s implementation. Rather than use a heretodoc string to represent an XML document, I depend on a class that speaks XML.
My code and designs have certainly improved over the years. To help reinforce and develop of my skills, I continually employ good programming and software engineering practices on the projects that involve me. I also analyze my approach and think about alternatives or ways that it can be made better.