Sponsored Link •
Say something about politics, people skills, leadership, listening.
James Gosling: "One of the most important parts of OO design is figuring out what the interfaces are."
No, Gosling's statement that "You can't sit alone in a room anymore and write software." That things will need to work together, and that requires interfaces. Interfaces include semantic contracts, which are a way for people to communicate with other people. Programmers to communicate with other programmers.
I assume readers of Flexible Java are already familiar, though not necessarily proficient, with the Java language. The books target intermediate Java programmers who want to become "better" Java programmers. They will help readers who are already familiar with the syntax and semantics of the Java language become more competent in actually using the Java language to solve real programming problems.
The Flexible Java Forum
Software design is subjective. Your idea of a well-designed program may be your colleague's maintenance nightmare. Because the people who will have to decipher your code and live with your design and coding decisions are your programming peers, I believe the development of design and coding guidelines and idioms should by guided by a discussion among the programmers who have to work with each other's code.
In light of this fact, I am trying to make the process of writing Flexible Java as interactive as possible. I'd like to make the book project a guided discussion about Java design.
I encourage your comments, criticisms, suggestions, flames -- all kinds of feedback -- about the material posted at this web site. If you disagree with something, or have something to add, please let me know.
A good place to give your feedback is the Flexible Java Forum, a discussion forum devoted to issues of Java design. Posting your comments in this forum enables you to argue not just with me, but also with other participants in the forum. What could be more fun?
The Design Techniques Column
As I write Flexible Java, I will be publishing a monthly column in JavaWorld based on material from the book. This column, named "Design Techniques," is currently appearing in JavaWorld.
Software Development Monkeys on Your Back
In the real world, as you work to design and implement software, you have several concerns to keep in mind -- several "monkeys on your back." Each monkey competes with the others for your attention, trying to convince you to take its particular concern to heart as you work. One large, heavy monkey hangs on your back with its arms around your neck and repeatedly yells, "You must meet the schedule!" Another monkey, this one perched on top of your head (as there is no more room on your back), beats its chest and cries, "You must accurately implement the specification!" Still another monkey jumps up and down on top of your monitor yelling, "Robustness, robustness, robustness!" Another keeps trying to scramble up your leg crying, "Don't forget about performance!" And every now and then, a small monkey peeks timidly at you from beneath the keyboard. When this happens, the other monkeys become silent. The little monkey slowly emerges from under the keyboard, stands up, looks you in the eye, and says, "You must make the code easy to read and easy to change." With this, all the other monkeys scream and jump onto the little monkey, forcing it back under the keyboard. With the little monkey out of site, the other monkeys return to their customary positions and resume their customary activities.
As you sit there in your cubicle and work on your software, to which monkey should you listen? Alas, in most cases you must listen to all of them. To do a "good job," you will need to find a way to keep all these monkeys happy -- to strike a proper balance between these often conflicting concerns.
This book is filled with guidelines to help you as you program in Java to bring happiness to one of those monkeys: the little one that hides under the keyboard, the monkey of flexibility.
The First Monkey: Meeting the Schedule
Often the most important monkey to your management will be the first monkey, meeting the schedule. The schedule is critical, of course, because commercial software development is done for commercial reasons.
Although this book is not about meeting the schedule, the guidelines of this book do not provide flexibility at the cost of schedule. In other words, although the guidelines of this book won't help you meet the schedule, they won't hurt your chances of meeting the schedule either. And later on, they may save you time and money in maintenance, robustness, etc.
The Second Monkey: Correctly Implementing the Specification
To appease the second monkey, the most important thing is doing the work up front to figure out and communicate a clear, sufficiently detailed vision of the end target. If you don't know where you are going, you'll likely end up somewhere else. To produce a product that matches and satisfies the customer's expectations, all those involved in developing the software must have a clear understanding of what they are supposed to produce.
Program correctness arises primarily from good communication within the organization, and secondly, from competent programmers who are able to take a specification and turn it into a program. As with meeting the schedule, the guidelines in this book won't help you achieve program correctness, but they won't hurt either.
The Third Monkey: Robustness
If you meet your milestones during the specification, design, and implementation phases but then require two more years to get the system to work reliably, you have a problem. Bugs are inevitable, but there is a threshold beyond which a product becomes unusable or at least less marketable. This threshold varies depending on the application. Software that helps commercial jetliners navigate likely has a higher robustness threshold than the browser software you use to surf the web. However, in all cases there is some level of robustness that you must deliver.
In my experience, robustness has arisen out of good, solid designs and good coding practices. With the advent of Java, with its garbage collection and limitations on pointers, robustness became much easier to deliver. But even without memory problems, bad design and bad coding can still yield programs with robustness troubles.
The guidelines of this book, even though they are aimed squarely at improving the flexibility of your software, may also have a side-effect of improving the robustness of your software.
The Fourth Monkey: Performance
Performance -- execution speed, resource usage, and so on -- is something you should usually keep in mind as you design and implement software. But often, programmers try to solve performance problems their programs don't actually have. The right approach typically is to keep performance in the back of your mind as you develop. In the front of your mind, keep "good object-oriented, thread safe" design. During integration and testing, if you discover you do indeed have a performance problem, that is the time to analyze and address it.
The guidelines in this book won't help you achieve better performing programs. In fact, the guidelines in this book may negatively impact your program's performance to some extent. Often
The Fifth Monkey: Flexibility
There is more than one path to correct implementation of a specification. Different teams of programmers can produce drastically different designs and implementations, all of which fulfill the requirements set forth by the specification. Some versions, however, may take longer to write and debug. As they execute, some versions may be slower than others. And over time, as the program evolves from release to release, some versions may turn out to be less flexible than others.
Flexibility, the ease with which a program can be changed, is important because source code usually evolves over time. As bugs are fixed and enhancements made for new releases, programmers must return to already-existing source code, understand it, and make changes to it. The easier code is to understand and change, the cheaper and faster it is to make those changes.
When you write a program, you are communicating. Most obviously, you are communicating with a machine. You are telling a computer what you want it to do. But there is another, less obvious, form of communication that you perform when you write a program: communication with other programmers. Through your source code, you communicate your design to any programmers who, in the future, ever need to fix a bug, make an enhancement, or simply reuse your code. You are telling programmers how your code is supposed to work, how it should be used, and how best to go about changing it.
One of the most important aspects of program flexibility is source code readability. Readable source code is important because it helps future programmers (possibly including your future self) understand what you were trying to do. In many cases in the real world, a program's source code is the documentation. When others work on your source code, either to add new features or to fix bugs, they read your source code to understand what it does.
What constitutes a "good object-oriented, thread-safe" design? The answer depends on whether you are talking about object orientation or thread safety.
Thread safety primarily is about robustness (monkey number three). If you forget to make methods synchronized that really need to be synchronized, you can end up with intermittent data corruption. Creating thread-safe designs involve understanding multithreading in general, understanding the multithreaded needs of your particular programs, and programming accordingly.
Good object-oriented design also plays a part in robustness. As I said earlier in this chapter, my experience has shown me that robustness arises out of good design and coding practices.
In addition, a good object-oriented design can play a role in keeping a project on schedule (monkey number one). As part of a solid overall software development process, a thorough design phase can help a team avoid unforeseen schedule slips. A thorough design gives the team a clear and detailed view of work that will be required by the implementation phase.
The primary benefit, however, of a good object-oriented design is flexibility, the concern of monkey number five. A good object-oriented design can give you code that is easy to understand and easy to change. And the greater the flexibility of a body of code, the quicker (and cheaper) will be enhancements and bug fixes to that code.
The guidelines put forth in this book will
primarily help you achieve program flexibility. When I
talk about object design, building class hierarchies,
interfaces, polymorphism, choosing composition vs. inheritance,
and so on, my main focus will be to give insights that will
help you make your programs easier to understand and
easier to change.
Before getting started, I'd like to clarify a few terms.
Designer vs. client programmers
Most Java programmers have two hats on their shelf, which they wear at different times. Sometimes they wear their "designer" hats and build libraries of classes for others to use; other times they wear their "client" hats and make use of a library of classes created by someone else. Some Java programmers even where both hats at the same time, completely oblivious to the rules of fashion.
One aspect of the flexibility of a body of code is the ease with which a client programmer can understand the code. Whether a client programmer is planning to change code or just use it as is, that programmer often has to figure out how to change or use the code by reading it.
The guidelines discussed in this book will talk about flexibility in terms of client programmers. Designs and implementations that are flexible are those that are easy for client programmers to understand, use, and change.
The first guideline
The guidelines proposed in this book are not proposed as laws you should blindly follow at all times but as rules of thumb you'll probably want to follow much of the time. They are intended to help you acquire a mindset conducive to good design. Thus, the first guideline is:
When designing and coding, do unto other programmers (who will be maintaining your code) as you would have them do unto you (if you were to maintain their code).
Hey, two more potential monkeys: code reuse and security.
Also, the software design process is a conversation. There's the code, which is like this magical text that is constantly growing and contracting, changing on the behest of a group of "elite high priests and priestesses" that know how to care for the thing. It is "a living product" as one of my managers used to say. It is like a beast you have to tame. And the group of folks working on the thing are also constantly in flux.
It is kind of like monks working on copying or even translating a holy text, but not really. It's not really like anything that's come before it. We are giving instructions to machines we have built and are surrounding ourselves with.
This process is very human. It is a conversation, a negotiation, a communication, a leadership and a followership. Sometimes, if you are working by yourself and the project is reasonably sized, a quick hack is in order. You start coding and can effectively design as you code. The more people are involved, the more important it is to have distinguishable design phase, or analysis and design phases.
How the design process is managed is a people problem. If I'm a manager, I've got to look at who I have at my disposal to attack a design. In some cases approach A is good. Other cases approach B. It all depends on the project and the people.
The design process is a process of communication. If you are working alone doing a hack, you must communicate with yourself. This shouldn't be a problem, because many programmers are accustomed to talking to themselves. Why, I was just telling myself that very thing the other day.
There are situations in which you know you'll have a performance problem. An example is if you are programming a smart card...
How rare it is that maintaining someone else's code is like entering a beautifully architected building, which you admire as you walk around and plan how to add a wing or do some redecorating. More often, maintaining someone else's code is like being thrown headlong into a big pile of someone else's discusting, smelly, slimy garbage. You have to find some way to rearrange the garbage to fix some bug or make an enhancement. You regularly make apalling discoveries that grate against your design sensibilities like fingernails being scraped down a blackboard. So the final justification for flexibility is simply a quality of life justification: I don't like swimming around in garbage, and you shouldn't either.
This book is about design above the level of coding style. Where to put that open curly, where to put a space, and so on, especially indentation, can make code readable. But I'm not going to talk about that very much, other than some talk of naming. Maybe I will have a section of quick coding guidelines.
If you are writing massive processing applications on mainframes or supercomputers, you need every once of performance you can get simply because of the almost unimaginable size of the computation. Nevertheless, you too could benefit from this book because even if you know you need to sacrifice flexibility, this book can help you understand what flexibility is and why it is important.
You can decide to sacrifice flexibility for performance, or security, or any other monkey you may happen to fancy. This book is about flexibility, it is most appropriate for developers who are developing software where resources are rather cheap, such as the PC. If you are developing for embedded devices, resources are more scarce and sacrificing flexibility for performance (or simply to make your program fit in memory or on ROM) makes more sense and may happen earlier in the process.
In this book, when I say client, I don't mean the code that invokes methods on an object, I mean the programmer who writes the code. If I really just mean the code, I say client code.
An object is a machine. Like any machine, an object offers services to "clients" in the outside world. When you invoke an instance method on an object, you ask that object to provide a service for you. The object either provides the service and returns from the instance method or, to indicate it was unable to provide the promised service, throws an exception back at you. You can think of the client of an object as being the code that actually invokes methods on the object. But I find it useful to think of the client as the programmer that writes the code. It is primarily the programmer, not the code, that you must think about when you design objects.
One of the early stages of of an object-oriented design is object discovery. During this stage, you divide up the required functionality of a system into many smaller areas of responsibility, each of which will correspond to an object in your solution. Object discovery yields named objects with defined areas of responsibility. Subsequently, you must design the interfaces of those objects, the method signatures and semantic contracts with which the objects will fulfill their responsibilities by offering services to clients.