Optimizing application development time with Java inheritance

Feature:

Optimizing application development time through effective Java inheritance

By Randall Nagy

TheServerSide.com

All development teams struggle with the question as to how to reduce the development effort by reusing code that has already been tested and verified. One tried and true method is to optimize application development by effectively using Java inheritance.

The ephemeral beauty of inheritance is that at some level, everything in the universe shares an  is-a relationship with something else. Educators attempting to explain the concept of inheritance to the uninitiated often explain the concept by discussing the relationship between groups of related animals. For example, collies, chihuahuas, and dobermans are all types of dogs. And following the same logic, it becomes obvious that a boxer is-a dog, as well. In Java and .NET, everything is-a software component of type Object at some level. From there on out however, what developers choose to relate together in sub and super class hierarchies is a matter of design.

All development teams struggle with the question as to how to reduce the development effort by reusing code that has already been tested and verified. 

Randall Nagy, Principle Engineer with Soft9000

In the problem domain of desktop rendering with Java, much thought has been put into grouping visual items together. From a parentage point of view, well-known graphical elements such as JDialog and JFrame, while different in notable ways, are all related to a much more general Window class. Anything we choose to do with the properties of a JDialog that are inherited from Window can  also be done with ancestors of JFrame, as they have the common Window class as an ancestor. Indeed, writing reusable code to manage many related child objects is the hallmark of excellent design. Like dogs and Windows, great polymorphic design is all about abstraction, triage, design, and inheritance.

The evolution of Shareanote and Jackhammer

A software developer at heart, I moonlight as a technical trainer, enjoying the opportunity to share my knowledge of technology with others. As a trainer, I often wished I had a flashcard tool to offer my students. In addition to having a set of home scholars under the roof, the recent need for many to review for tests and share notes inspired me to write a flashcard sharing and management program. Interestingly, while writing that same program, I also discovered that I had inadvertently created a relatively decent note-taking and file-sharing program as well. So while there was really one application at the heart of the two or three different products that were evolving, three separate products were emerging,with each product having a different name, each product would have a different version number, dialog views would be different and there would be different GUI names for problem domain controls. For example, while labeling buttons Create Note in the Shareanote application, that same GUI Elements would be called Create Flash Card in Jackhammer. To minimize the coding effort while reusing already developed software, the solution would simply be to provide one string for the original Flashcard software, and another string for the note-taking product.

public enum Skinner {
ShareNote, // Note Taker Names & Features
Jackhammer; // Flash Card Names & Features
}

For developers who have practical experience with modern GUI design tools, it is known that simply changing product names and versions would make even using resource bundles overkill. Yet who wants to write all of that code for updating each control in every JFrame or JDialog? While color is a high parent capability, textual settings are much lower in the GUI parentage problem domain.

As developers, we want to work smarter, not harder. Could recursion also be a good choice for managing product names, version information, and other tokens, too?

   public void skin(Window win) {
if (win == null) {
return;
}
Component[] parent = win.getComponents();
for (Component comp : parent) {
skin(comp);
}
}

Like lightning stringing twice, the hierarchical recursion technique works so well that it needs to be shared. Here is the recursion technique in action:

public enum Skinner {
   ShareNote(Notebook, Note, Free, Shareanote, 1.0),
Jackhammer(Deck, Card, Free, Jackhammer, 1.0);
private String nameFile;
private String nameRecord;
private String nameEdition;
private String nameProduct;
private String version;
public static final String TOKEN_FILE = %file;
public static final String TOKEN_RECORD = %record;
public static final String TOKEN_PRODUCT = %product;
public static final String TOKEN_VERSION = %version;
public static final String TOKEN_EDITION = %edition;
private TitledBorder tborder; Skinner(String nameFile, String nameRecord, String nameEdition, String nameProduct, String versionProduct) {
this.nameFile = nameFile;
this.nameRecord = nameRecord;
this.nameEdition = nameEdition;
this.nameProduct = nameProduct;
this.version = versionProduct;
} public void skin(Frame win) {
if (win == null) {
return;
}
win.setTitle(skin(win.getTitle()));
skin((Window) win);
JDialog dlg = null;
} public void skin(Dialog win) {
if (win == null) {
return;
}
win.setTitle(skin(win.getTitle()));
skin((Window) win);
} public void skin(Window win) {
if (win == null) {
return;
}
Component[] parent = win.getComponents();
for (Component comp : parent) {
skin(comp);
}
} public void skin(Component comp) {
if (comp == null) {
return;
} if (comp instanceof AbstractButton) {
AbstractButton jcomp =(AbstractButton) comp;
jcomp.setText(skin(jcomp.getText())); } if (comp instanceof JMenuItem) {
JMenuItem ref = (JMenuItem)comp;
MenuElement[] ele = ref.getSubElements();
for(MenuElement eref : ele){
skin(eref.getComponent());
}
}
if (comp instanceof JComponent) {
JComponent jcomp = (JComponent)comp;
Border border =jcomp.getBorder();
if (border != null) {
if(border instanceof TitledBorder) {
tborder= (TitledBorder) border;
tborder.setTitle(skin(tborder.getTitle()));
}
}
Component[] set =jcomp.getComponents();
for (Component ref : set) {
skin(ref);
}
}
} public String skin(String subject) {
if (subject == null) {
return ;
}
String result = com.soft9000.Text.ReplaceAll(subject,TOKEN_FILE, this.getNameFile());
result = com.soft9000.Text.ReplaceAll(result,TOKEN_RECORD, this.getNameRecord());
result = com.soft9000.Text.ReplaceAll(result,TOKEN_PRODUCT, this.getNameProduct());
result = com.soft9000.Text.ReplaceAll(result,TOKEN_VERSION, this.getVersion());
result = com.soft9000.Text.ReplaceAll(result,TOKEN_EDITION, this.getNameEdition()); return result;
} /* Setters and getters removed */
}

The powers of recursion

Reviewing the above code, witness first that we are using an enum, rather than a class. While the distinction between class and enum might seem trivial at  first blush, note that each enumerated value is an instance of the enumerated construct itself. The only difference between each enumerated instance is that the self-reference contains a unique token-name as well as an ordinal.

Also note that we are using global constants such as TOKEN_FILE (%file) and TOKEN_RECORD (%record), rather than any enumerated value. Those tokens are what Skinner looks for in the GUI.

Above, as their enames depict, the task of the Skinner is to recursively search through all of the places where we have imbedded those constants. For Window, Frame, and Dialog inheritance trees alike, once any of the tokens have been found, they will be replaced with any appropriate product-skin values.

Once the first test case was run, the verdict was in. By using Skinner on any of the instanced objects, from the AbstractButton to the TitledBorder, we can quickly have a single code base for any TOKEN_ tagged product:

   public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
GuiMainmain = new GuiMain(Skinner.ShareNote);
main.setVisible(true);
}
});
}

Better still, to switch skins, all one has to do is to copy the above main method to another project, include the previous project's JAR files and then change Skinner.ShareNote to Skinner.Jackhammer. Once inside the program, those skin() operations are overloaded and readily accessible via the above provided enumerated instance itself.

Conclusion

While most items are readily converted to the big parent classes such as Window, Component, or JComponent, interface based components can simply use reflection to participate in any overloaded skin() operations. Interface and inheritance based paradigms mean that several Interfaces, such as MenuElement in the above code, must instead have their own getComponent() permutations. Feel free to locate and add them to your own Skinners as requirements evolve.

How have you used inheritance and interfaces to simplify application development? Let us know.

20 Jul 2013

Related Content

Related Resources