Many of the problems you face in the design phase can be easily solved. Here is a technique that leverages un-extended JavaDoc with Marker Interfaces to tie the "what" and "how" of the code to the "why".
What are Design Markers?
Design Markers are Marker Interfaces that are used primarily for documentation purposes and specifically to document Design Choices. The description of each design choice is placed in the JavaDoc comments of its associated design marker interface in as much detail as desired.
I've coined the term Design Choices to encompass all flavors of design contracts because it emphasizes an oft forgotten fact that design is not a checklist of "best practices", but rather an interacting set of choices. These choices balance tradeoffs between competing design patterns that are made for the benefit of an entire system rather than isolated classes.
Marker Interfaces are defined as those that have no details, only a name. This idea has long been used in Java via standard interfaces like Serializable, Clonable, RandomAccess, etc. However, their use has typically been limited to those interfaces intended to be explicitly verified at runtime, normally via "instanceof".
Design Markers, even though focused on documentation, share the root purpose of marker interfaces; namely, to declare adherence to a design contract whose implementation cannot be policed by the language compiler and runtime system. For example, just because a class declares "implements Serializable" doesn't mean that it has correctly implemented the Serializable contract, but since Java can't really tell if the contract has been met, using the marker interface is more of an explicit pledge by the programmer that it has.
The overlooked benefit of marker interfaces is that they also document the intention that a contract should be met. Generalizing this notion to any design choice, Design Markers document the required obligations of a class and the reasons why. An alternative notion that the source code alone can/should be the documentation has two problems here:
- If the implementation of a contract (even a well-known one) is more complicated than a few simple assert statements, it is easy to not recognize that a contract is being implemented at all, much less which one.
- Even when a contract can be inferred from the implementation source, it is not clear why that contract is being implemented. In other words, was a class made Serializable just because the programmer felt like it, or is it a requirement of some encompassing system design?
Since interfaces in Java can inherit multiple parent interfaces, Design Markers benefit from being able to define higher level contracts in terms of lower level ones. For example, a "Singleton" design marker can be created to mark classes that are to implement that well-known design pattern. Additionally, Sun's J2EE pattern, "Service Locator", which usually requires that the Service Locator be a singleton, can be enshrined as a design marker that inherits the Singleton marker interface. Thus, any class marked as a ServiceLocator is automatically documented as needing to be a Singleton. So, not only has the fact that a class must be a singleton been established, but also the reason why.
When creating the JavaDoc comments attached to each interface, one can put more detail than typical because the comments do not need to be repeated anywhere else. Traditionally, even if each singleton class had a comment noting that it was a singleton, any elaboration (much less tutorial information) would not have been added because it would have needed to be copied repeatedly (or worse, it would have been placed sporadically in arbitrary spots). Design Markers define a well-known place to factor out all those details and encourage including design documentation. JavaDoc automatically publishes all this in a form that makes it trivial to navigate from each use of a design pattern to that pattern's central documentation and vice versa. This is especially useful for project-specific or lesser known or newly hatched patterns, idioms, conventions, etc.
For example, consider Data Transfer Objects (DTOs) that are passed as EJB remote method parameters. Since copies of these parameters are passed, rather than Java's normal pass-by-reference, it is common to make them "immutable". Since one can't simply declare class Foo to be immutable in Java, a series of conventions must be programmed. However, with a design marker, the intention that a class should be immutable is a simple declaration (e.g. class Foo implements Immutable). Even if a pattern is well known, particular variants could be specified. For example, the design marker's JavaDoc comments could explain: "a variation is being used where no "get" methods are defined since users can safely access the property fields directly because each must be declared public and final".
To extend the example, suppose for EJB method return values, an extra restriction was desired to prevent counterfeits. One could coin the term "consume-only" to mean "classes whose instances can be read-by but not created-by the user". A ConsumeOnly design marker interface is created to document that concept. Further, to codify the entire collection of contracts associated with return values, one could declare, "Interface ReturnValue extends ConsumeOnly, Immutable" thus documenting both a new contract and its relation to other contracts. The JavaDoc for each design marker interface can document its definition, how it needs to be implemented, why it is done, etc. And instead of redundantly copying this info into the comments of each return value DTO class (or leaving it undocumented altogether), each need only declare, "implements ReturnValue".
Since markers are like little sticky notes attached to individual classes, they work well for "adjective patterns" like Immutable, Serializable, Singleton, etc. However, relationships between multiple classes can still be documented by defining each of the roles in that relationship as Design Markers. The roles could even inherit a master marker for the overall pattern. For example, the MVC pattern defines three roles (Model, View, & Controller), and each can be defined as a Design Marker. Each could also inherit an MVC marker in which to place documentation about the pattern as a whole and the relationships between its roles.
Finally, as a "best practice", I suggest making all design marker interfaces an extension of a root level design marker (e.g. Contract). This has two benefits: (1) making a place to document the project's practice of using Design Markers overall, and, (2) making it trivial, via JavaDoc, to get a hyperlinked catalog of all your defined contracts. New project members will love you.
How do Design Markers compare to other methods for the problems they solve?
Today, design and implementation are expressed in different languages, and this is the root problem that Design Markers attack. While implementation is embodied in artifacts like source code, build scripts, and configuration files, design is kept in artifacts like UML diagrams, design models, or even primitive word processor documents. Because they can't express design choices directly in their implementation languages, implementers must use manually enforced programming conventions, and so naturally, they need to always be cognizant of those design choices. However, without an expensive round-trip design tool, "the blueprints" are not typically at hand (or even kept up to date). Because design choices are spillage in the shuffle between design and implementation, developers are often unaware of them and therefore fail to maintain them. The problem only gets worse over time as development mode turns into maintenance mode, often with personnel turnover. Finally, since this problem is just a special case of the general problem of human communications, its severity grows rapidly with project size.
So, design choices go unknown by the harried new hire bug fixer. The self-assured experienced developer who is mightily refactoring code is not reminded of why his seemingly more optimal version of a class, never the less, breaks the overall system. Moreover, while testing might discover broken contracts after the fact, it is much better to prevent them. Good programming practice says that things requiring synchronization should be proximate to each other in the source code. There is nothing in more need of synchronization than "what the design decrees" and "how and where the implementation fulfills it".
A special mention must be made about the problems associated with refactoring in the absence of design markers. Often programmers "clean up code" while adding a new feature or fixing a bug, even though they were never "briefed" on the design choices governing that code. Even those who were briefed don't have perfect memories. Reasonable programmers will make locally appropriate, but globally inappropriate, changes when they are not conscious of the tradeoffs made for the good of the entire system. Worse, the consequences of breaking a design contract may be not be a functionality failure, but instead a performance failure. Since unit tests focus on testing functionality much more than performance, these defects may not be discovered. In addition, even changes that improve the performance of an individual unit may, in fact, hurt the performance of the entire system. And this regression is even less likely to be caught because system level tests (like integration and acceptance tests) are done much less than unit tests. Again, preventing breaks is much better than trying to catch them later via testing. Having design markers, not only makes developers aware of pre-existing contracts, but provides a good trigger for a design and/or code review, namely, the changing of a design marker (i.e. a contract) which, after all, is literally the same as changing the published interface.
So, why aren't design choices recorded in source code more often? Mostly, because there has been no clear place to put them. The extant choices are (1) simple comments, (2) custom JavaDoc tag comments, and (3) proprietary markup added by round-trip design tools. If you have the budget for fancy tools and their associated developer training, more power to you. If not, or if you want to use a more portable mechanism, there are comments. However, comments, whether plain or in JavaDoc tag form, do not provide the Design Marker benefits discussed ahead.
A variation on simple comments is to invent and use custom JavaDoc tags (e.g. @Immutable, or @TypeSafeEnum). This approach is given more encouragement with the new JDK 1.4 version of JavaDoc that allows simple command line parameters to make JavaDoc aware of your invented tags and produce "official" documentation in the generated HTML. However, the HTML generated by JavaDoc is still just text (albeit with nicer paragraph headers). A Design Marker (like all Java Interfaces) causes JavaDoc to generate hyperlinks back and forth between its definition and the definitions of each of its heirs. While arbitrarily elaborate processing of your custom JavaDoc tags can be had if you learn the Doclet framework & API, and write custom Doclet plug-ins, this option does require tool development time, and therefore, money.
A summary of benefits of Design Markers over JavaDoc tags follows:
- Design Markers have no start up costs (unlike Doclet plug-ins).
- Marker interfaces are automatically inherited and therefore automatically referenced on all subclasses, so JavaDoc provides hyperlinks to all documented requirements. JavaDoc custom tags aren't inherited.
- JavaDoc creates hyperlinks to/from each class and its inherited interfaces, thus navigation to/from uses of Immutable and the definition of Immutable is trivial. JavaDoc custom tags (e.g. @Immutable) do not generate hyperlinks, only simple text.
- Custom JavaDoc tags require JDK 1.4 and special JavaDoc command line parameters to declare them. Design Markers require no special version of JavaDoc and no special command line parameters.
- While meant for documentation, Design Markers, never the less, also allow test suites to verify design compliance by testing for the markers at runtime. For example, if certain return values from an API are supposed to be "consume only", then checking for "instanceof ConsumeOnly" can be added to those tests.
As a simple example of using Design Markers and illustrating the JavaDoc produced for them, I have placed four Java source files (Abelian.java, AddrType.java, Contract.java, and TypeSafeEnum.java) and all the JavaDoc that is generated for them (starting with index.html) at http://www.polyglotinc.com/designMarkerExample/ and they can be viewed and/or downloaded from there. Whether with JDK 1.3 or 1.4, the JavaDoc command that was used is simply "javadoc *.java".
About the Author
As principle consultant of PolyGlot, Inc., Bruce Wallace
has provided consulting and custom computer software development services around the world. Projects have been completed in Sydney Australia, Perth West Australia, "Silicon Valley" California, "Route 128" Massachusetts, Austin Texas, and currently in Atlanta Georgia. Bruce has been developing with Java since 1996, Object Orientation since 1988, and Software Engineering since 1974. During his career, he has covered the full spectrum of roles from manual QA tester through CTO, even in organizations you've heard of like Apple Computer, Sun-Javasoft, Hewlett-Packard, Coca-Cola, and BellSouth. Bruce's first published article was way back in the May 1981 issue of Byte magazine, and his most recent was in the March 2001 issue of JavaPro magazine [http://www.polyglotinc.com/reflection.html].
©Copyright, 2002-2003, PolyGlot, Inc.