J2EE projects tend to be complex. This makes good coding practices vital. In this chapter, we've looked at how good OO practice underpins good J2EE apps. We've also looked at the importance of consistently applying sound coding standards, to allow efficient teamwork and help to ensure that apps are easy to maintain. Finally, we've discussed how to avoid writing code, through use of existing frameworks and – in the last resort – the implementation of our own frameworks. The following table summarizes the OO design principles we've discussed:
Technique |
Advantages |
Disdvantages |
Related design patterns |
Impact on performance |
---|---|---|---|---|
Code to interfaces, not concrete classes. The relationship between app components should be in terms of interfaces, not classes. |
Promotes design flexibility. Works well when interfaces are implemented by JavaBeans, configured through their bean properties.
|
Marginally more complex to implement than use of concrete inheritance. |
Many design patterns are based on interface inheritance. |
Negligible |
Prefer object composition to concrete inheritance. |
Promotes design flexibility.
|
May lead to an increased number of classes.
|
Strategy (GoF) |
None |
Use the Template Method design pattern when you know how to implement a workflow but not how all individual steps should be implemented. |
Ensures that the workflow can be implemented and tested once.
|
Sometimes delegation is a better model, and the Strategy pattern is preferable. |
Template Method (GoF) |
None |
Use the Strategy design pattern as an alternative to the Template Method pattern when the flexibility of delegation, rather than concrete inheritance, is desirable. |
There's greater freedom when implementing the interface than using concrete inheritance.
|
Slightly more complex to implement than the Template Method pattern, which is often an alternative. |
Strategy (GoF) |
None |
Use callback methods to achieve extensibility while centralizing workflow. |
Can achieve code reuse when other approaches can't deliver it.
|
Conceptually complex, although code using it is generally simpler than it would be using other approaches. |
A special case of the Strategy design pattern (GoF) |
Slight performance degradation if the callback interface is invoked very often |
Use the Observer design pattern. |
Promotes separation of concerns by decoupling listeners from the execution of business logic that generates events.
|
Introduces complexity that isn't always warranted.
|
Observer (GoF) |
Having too many observers (listerns) can slow a system down. |
Combine multiple method arguments into a single object. |
Allows use of the Command design pattern.
|
Increases the number of objects in a system. |
Command (GoF) EJB Command (EJB Design Patterns) |
Contributes to "object churn." In relatively infrequent calls such as EJB invocation, the cost of the necessary object creation is negligible. In a nested loop, the cost might be severe. |
Use unchecked exceptions for unrecoverable errors, and checked exceptions when calling code is likely to be able to handle the problem. |
Less code.
|
Many Java developers are used to using checked exceptions almost exclusively.
|
All |
None |
Use reflection. |
A powerful way to parameterize Java code.
|
Reflection can be overused. Sometimes a simpler solution is equally effective. |
Factory (GoF) |
Depends on how often calls are made. Usually there is no significant effect. |
Implement app components as JavaBeans. |
Makes it easier to configure systems declaratively, consistent with J2EE deployment approach. 174Allows problems such as input validation to be addressed using the standard JavaBeans API. |
All |
Usually negligible. |
|
Avoid a proliferation of singletons by using an app context or registry. |
Promotes design flexibility.
|
Registry will require configuration outside Java, such as an XML document. This is an excellent approach for complex apps, but unnecessary for very simple apps. |
Singleton (GoF) Factory
|
None |
Technique |
Advantages |
Disdvantages |
Impact on performance |
---|---|---|---|
Start from JavaSoft's coding conventions. |
Makes it easier for new developers to read your code. Familiarity with Sun's conventions makes it easier for you to read the code of others. |
None |
|
Objects and methods should have clear responsibilities. |
Makes code self-documenting.
|
None |
|
Avoid literal constants in code. |
Makes it easier to read and maintain code.
|
None |
None |
Use only private instance variables.
|
Favors black-box class reuse and loose coupling.
|
Using private instead of protected instance variables reduces the ability of subclasses to modify superclass behavior. However, this is normally a good thing. |
Negligible performance overhead in the use of methods, rather than direct variable access. |
Keep a class's public interface to a minimum. |
Helps to achieve to loose coupling between classes.
|
None |
None |
Use final methods appropriately. |
Final methods can be used to prevent subclasses incorrectly modifying superclass behavior by overriding methods. |
Limits the scope of subclasses to customize superclass behavior. However, overriding concrete methods is a poor way to achieve extensibility. |
Marginal improvement, as the JVM knows which class the method is defined in. |
Implement toString() methods useful during debugging and maintenance. |
If all classes have toString() methods debugging is a lot easier, especially when combined with a sound logging strategy. |
None |
toString() methods can be costly to invoke, so it's important to ensure that they're not invoked unnecessarily (for example, by the generation of logging messages that won't be output). |
Eliminate code duplication. |
Code duplication is disastrous for maintenance and usually reflects time wasted in development.
|
None |
None |
Don't publicly expose untyped collections where an array could be used. |
Helps make code self-documenting and removes one possibility of incorrect usage.
|
Sometimes converting data to an array type is awkward or slow, or we require a collection (for example, to enable lazy materialization). |
Neutral. If it's slower to convert a collection into an array, it's probably not a good idea to use this approach. |
Document code thoroughly |
Code that isn't throroughly documented is unfinished and potentially useless. The standard Javadoc tool is the cornerstone of our documentation strategy. |
None |
None |
Instrument code with logging output. |
Enormously helpful during debugging and maintenance.
|
None, if logging is implemented properly. |
Careless implementation of logging, or misconfiguration of a logging system, may reduce performance. However, this can be avoided by generating log messages only if we know they'll be displayed. |
In the next chapter we'll move from the theoretical to the practical, looking at the business requirements for the sample app that we'll discuss throughout the rest of this tutorial.