Thinking in Java, 3rd ed. Revision 4.0
[ Viewing Hints ] [ Book Home Page ] [ Free Newsletter ]
[ Seminars ] [ Seminars on CD ROM ] [ Consulting ]
This appendix contains suggestions to help guide you in performing low-level program design and in writing code.
Naturally, these are guidelines and not rules. The idea is to use them as inspirations and to remember that there are occasional situations where they should be bent or broken. Feedback
- Elegance always pays off. In the short term it might seem like it takes much longer to come up with a truly graceful solution to a problem, but when it works the first time and easily adapts to new situations instead of requiring hours, days, or months of struggle, youll see the rewards (even if no one can measure them). Not only does it give you a program thats easier to build and debug, but its also easier to understand and maintain, and thats where the financial value lies. This point can take some experience to understand, because it can appear that youre not being productive while youre making a piece of code elegant. Resist the urge to hurry; it will only slow you down. Feedback
- First make it work, then make it fast. This is true even if you are certain that a piece of code is really important and that it will be a principal bottleneck in your system. Dont do it. Get the system going first with as simple a design as possible. Then if it isnt going fast enough, profile it. Youll almost always discover that your bottleneck isnt the problem. Save your time for the really important stuff. Feedback
- Remember the divide and conquer principle. If the problem youre looking at is too confusing, try to imagine what the basic operation of the program would be, given the existence of a magic piece that handles the hard parts. That piece is an objectwrite the code that uses the object, then look at the object and encapsulate its hard parts into other objects, etc. Feedback
- Separate the class creator from the class user (client programmer). The class user is the customer and doesnt need or want to know whats going on behind the scenes of the class. The class creator must be the expert in class design and write the class so that it can be used by the most novice programmer possible, yet still work robustly in the application. Think of the class as a service provider for other classes. Library use will be easy only if its transparent. Feedback
- When you create a class, attempt to make your names so clear that comments are unnecessary. Your goal should be to make the client programmers interface conceptually simple. To this end, use method overloading when appropriate to create an intuitive, easy-to-use interface. Feedback
- Your analysis and design must produce, at minimum, the classes in your system, their public interfaces, and their relationships to other classes, especially base classes. If your design methodology produces more than that, ask yourself if all the pieces produced by that methodology have value over the lifetime of the program. If they do not, maintaining them will cost you. Members of development teams tend not to maintain anything that does not contribute to their productivity; this is a fact of life that many design methods dont account for. Feedback
- Automate everything. Write the test code first (before you write the class), and keep it with the class. Automate the running of your tests through a build toolyoull probably want to use Ant, the defacto standard Java build tool. This way, any changes can be automatically verified by running the test code, and youll immediately discover errors. Because you know that you have the safety net of your test framework, you will be bolder about making sweeping changes when you discover the need. Remember that the greatest improvements in languages come from the built-in testing provided by type checking, exception handling, etc., but those features take you only so far. You must go the rest of the way in creating a robust system by filling in the tests that verify features that are specific to your class or program. Feedback
- Write the test code first (before you write the class) in order to verify that your class design is complete. If you cant write test code, you dont know what your class looks like. In addition, the act of writing the test code will often flush out additional features or constraints that you need in the classthese features or constraints dont always appear during analysis and design. Tests also provide example code showing how your class can be used. Feedback
- All software design problems can be simplified by introducing an extra level of conceptual indirection. This fundamental rule of software engineering is the basis of abstraction, the primary feature of object-oriented programming. In OOP, we could also say this as: If your code is too complicated, make more objects. Feedback
- An indirection should have a meaning (in concert with guideline 9). This meaning can be something as simple as putting commonly used code in a single method. If you add levels of indirection (abstraction, encapsulation, etc.) that dont have meaning, it can be as bad as not having adequate indirection. Feedback
- Make classes as atomic as possible. Give each class a single, clear
purposea cohesive service that it provides to other classes. If your
classes or your system design grows too complicated, break complex classes into
simpler ones. The most obvious indicator of this is sheer size; if a class is
big, chances are its doing too much and should be broken up.
Clues to suggest redesign of a class are:
1) A complicated switch statement: consider using polymorphism.
2) A large number of methods that cover broadly different types of operations: consider using several classes.
3) A large number of member variables that concern broadly different characteristics: consider using several classes.
4) Other suggestions can be found in Refactoring: Improving the Design of Existing Code by Martin Fowler (Addison-Wesley 1999). Feedback
- Watch for long argument lists. Method calls then become difficult to write, read, and maintain. Instead, try to move the method to a class where it is (more) appropriate, and/or pass objects in as arguments. Feedback
- Dont repeat yourself. If a piece of code is recurring in many methods in derived classes, put that code into a single method in the base class and call it from the derived-class methods. Not only do you save code space, but you provide for easy propagation of changes. Sometimes the discovery of this common code will add valuable functionality to your interface. A simpler version of this guideline also occurs without inheritance: If a class has methods that repeat code, factor that code into a common method and call it from the other methods. Feedback
- Watch for switch statements or chained if-else clauses.
This is typically an indicator of type-check coding, which means that you
are choosing what code to execute based on some kind of type information (the
exact type may not be obvious at first). You can usually replace this kind of
code with inheritance and polymorphism; a polymorphic method call will perform
the type checking for you and allow for more reliable and easier extensibility.