BUY THIS BOOK
Add to Cart

Print Book $34.95


Add to Cart

PDF $27.99

Safari Books Online

What is this?

Add to UK Cart

Print Book £24.95

What is this?

Looking to Reprint or License this content?


Better, Faster, Lighter Java
Better, Faster, Lighter Java

By Bruce A. Tate, Justin Gehtland
Book Price: $34.95 USD
£24.95 GBP
PDF Price: $27.99

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: The Inevitable Bloat
Java development is in crisis. Though Java's market share has been steadily growing, all is not well. I've seen enterprise Java development efforts fail with increasing regularity. Even more alarming is that fewer and fewer people are surprised when things do go wrong. Development is getting so cumbersome and complex that it's threatening to collapse under its own weight. Typical applications use too many design patterns, too much XML, and too many Enterprise JavaBeans. And too many beans leads to what I'll call the bloat.
I'll illustrate the bloat by comparing it with the famous Lewis and Clark expedition. They started with a huge, heavily loaded 55-foot keel boat. Keel boats were well designed for traversing massive rivers like the Missouri and the Mississippi, but quickly bogged down when the expedition needed to navigate and portage the tighter, trickier rivers out West. Lewis and Clark adapted their strategy; they moved from the keel boats to canoes, and eventually to horseback. To thrive, we all must do the same. Java has not always been hard, and it doesn't have to be today. You must once again discover the lighter, nimbler vessels that can get you where you need to go. If the massive, unwieldy frameworks hinder you, then don't be afraid to beach them. To use the right boat, you've got to quit driving the bloat.
Over time, most successful frameworks, languages, and libraries eventually succumb to bloat. Expansion does not happen randomly—powerful forces compel evolution. You don't have to accept my premise blindly. I've got plenty of anecdotal evidence. In this chapter, I'll show you many examples of the bloat in applications, languages, libraries, frameworks, middleware, and even in the operating system itself.
Java developers live with a painful reality: huge enterprise frameworks are en vogue. That might be good news to you if you're among the 10% of Java developers who are working on the hardest problems, and your applications happen to fit those enterprise frameworks perfectly. The rest of us are stuck with excruciating complexity for little or no benefit. Successful J2EE vendors listen to the market:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Bloat Drivers
I'll illustrate the bloat by comparing it with the famous Lewis and Clark expedition. They started with a huge, heavily loaded 55-foot keel boat. Keel boats were well designed for traversing massive rivers like the Missouri and the Mississippi, but quickly bogged down when the expedition needed to navigate and portage the tighter, trickier rivers out West. Lewis and Clark adapted their strategy; they moved from the keel boats to canoes, and eventually to horseback. To thrive, we all must do the same. Java has not always been hard, and it doesn't have to be today. You must once again discover the lighter, nimbler vessels that can get you where you need to go. If the massive, unwieldy frameworks hinder you, then don't be afraid to beach them. To use the right boat, you've got to quit driving the bloat.
Over time, most successful frameworks, languages, and libraries eventually succumb to bloat. Expansion does not happen randomly—powerful forces compel evolution. You don't have to accept my premise blindly. I've got plenty of anecdotal evidence. In this chapter, I'll show you many examples of the bloat in applications, languages, libraries, frameworks, middleware, and even in the operating system itself.
Java developers live with a painful reality: huge enterprise frameworks are en vogue. That might be good news to you if you're among the 10% of Java developers who are working on the hardest problems, and your applications happen to fit those enterprise frameworks perfectly. The rest of us are stuck with excruciating complexity for little or no benefit. Successful J2EE vendors listen to the market:
  • Vendors can charge mega-dollars for mega-frameworks. Selling software means presenting the illusion of value. Big companies have deep pockets, so vendors build products that they can sell to the big boys.
  • It's hard to compete with other mega-frameworks if you don't support the same features. Face it. Software buyers respond to marketing tally sheets like Pavlov's dogs responded to the dinner bell.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Options
There are many possible solutions for dealing with the bloat in Java. Head-on is but one possibility. It takes courage and energy to take on the bloat, and you may not wish to fight this battle. You've got alternatives, each with a strong historical precedent:
Change nothing; hope that Java will change
This strategy means letting your productivity and code quality slide. Initially, this is the option that most developers inevitably choose, but they're just delaying the inevitable. At some point, things will get too hard, and current software development as we know it will not be sustainable. It's happened before, and it's happening now. The COBOL development model is no longer sufficient, but that doesn't keep people from slogging ahead with it. Here, I'm talking about the development model, not the development language. Java development is just now surpassing COBOL as the most-used language in the world, begging the question, "Do you want to be the COBOL developer of the 21st century?"
Buy a highly integrated family of tools, frameworks, or applications, and let a vendor shield you from the bloat.
In this approach, you try to use bloat to your best advantage. You may put your trust in code generation tools or frameworks that rely on code generation, like EJB, Struts, or Model Driven Architecture (MDA). You're betting that it can reduce your pain to a tolerable threshold, and shield you from lower-level issues. The idea has some promise, but it's dangerous. You've got to have an incredible amount of foresight and luck to make this approach succeed. If you previously bet big on CORBA or DCE, then you know exactly what I mean.
Quit Java for another object-oriented language.
Languages may have a long shelf-life, but they're still limited. For many, the decision to switch languages is too emotional. For others, like author Stuart Halloway, the decision is purely pragmatic. The long-time CTO of the respected training company DevelopMentor and tireless promoter of their Java practice recently decided to choose Objective C for an important project because Java was not efficient enough for his needs. Alternatives are out there. C# has some features that Java developers have long craved, like
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Five Principles for Fighting the Bloat
You can't fight the bloat by being simple-minded. You can't simply fill your programs with simple cut-and-paste code, full of bubble sorts and hardwiring. You cannot forget everything you've learned to date. It's an interesting paradox, but you're going to need your creativity and guile to create simple but flexible systems. You've got to attack the bloat in intelligent ways.
The bloat happened because the extended Java community compromised on core principles. Many of these compromises were for good reasons, but when core principles slide often enough, bad things happen. To truly fight the bloat, you've got to drive a new stake in the ground, and build a new foundation based on basic principles. You've got to be intentional and aggressive. In this book, I'll introduce five basic principles. Together, they form a foundation for better, faster, lighter Java.
Good programmers value simplicity. You've probably noticed a resurgence of interest in this core value, driven by newer, Agile development methods like eXtreme Programming (XP). Simple code is easier to write, read, and maintain. When you free yourself with this principle, you can get most of your code out of the way in a hurry, and save time for those nasty, interesting bits that require more energy and more attention. And simple code has some more subtle benefits as well. It can:
  • Give you freedom to fail. If your simple solution doesn't work, you can throw it away with a clear conscience: you don't have much invested in the solution anyway.
  • Make testing easier. Testability makes your applications easier to build and more reliable for your users.
  • Protect you from the effects of time and uncertainty. As time passes and people on a project change, complex code is nearly impossible to enhance or maintain.
  • Increase the flexibility of your team. If code is simple, it's easier to hand it from one developer to the next.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
In this book, I'm going to take my own medicine. I'll keep it simple and short. At this point, you're probably wondering how five simple principles can change anything at all. Please indulge me. In the pages to come, I'll lay out the five simple principles. I'll then show you the ideas in practice. You'll see how two successful and influential frameworks used these principles, and how to build applications with these frameworks. You'll see an example of a persistent domain model, an enterprise web application, a sophisticated service, and extension using these core concepts. My plan is simple. I'll show you a handful of basic principles. I'll show you how to succeed with the same ideas to build better, faster, lighter Java.
If you tend to value a book by the weight of its pages, go find another one. If you'd rather weigh the ideas, then welcome aboard. It all begins and ends with simplicity. And that's the subject of Chapter 2.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: Keep It Simple
Simplicity should be a core value for all Java programmers, but it's not. Most developers have yet to establish simplicity as a core value. I'll never forget when one of my friends asked for a code review and handed me a nine-page, hideously complex blob with seemingly random Java tokens. All kinds of thoughts swarmed through my mind in a period of seconds. At first, I thought it was a joke, but he kept staring expectantly. My next thought was that he hated me; I couldn't think of anything I'd done to deserve it. Finally, I began to read. After three pages of pure torture, I glanced up. He was grinning from ear to ear. My slackened jaw fell open, and I finally realized that he was proud of this code.
It's a cult. If you've coded for any length of time, you've run across someone from this warped brotherhood. Their creed: if you can write complicated code, you must be good.
Simplicity may be the core value. You can write simple code faster, test it more thoroughly with less effort, and depend on it once it's done. If you make mistakes, you can throw it away without reservation. When requirements change, you can refactor with impunity. If you've never thought about simplicity in software development before, let's first talk about what simplicity is not:
  • Simple does not mean simple-minded. You'll still think just as hard, but you'll spend your energy on simplicity, elegance, and the interactions between simple components. e=mc2 is a remarkably simple formula that forms the theory of relativity, one of the most revolutionary ideas ever.
  • Simple code does not necessarily indicate simple behavior. Recursion, multithreading, and composition can let you build applications out of simple building blocks with amazingly complex behavior.
  • Writing simple code does not mean taking the easy way out. Cutting and pasting is often the fastest way to write a new method, but it's not always the simplest solution, and rarely the best solution. Simple code is clean, with little replication.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Value of Simplicity
Simplicity may be the core value. You can write simple code faster, test it more thoroughly with less effort, and depend on it once it's done. If you make mistakes, you can throw it away without reservation. When requirements change, you can refactor with impunity. If you've never thought about simplicity in software development before, let's first talk about what simplicity is not:
  • Simple does not mean simple-minded. You'll still think just as hard, but you'll spend your energy on simplicity, elegance, and the interactions between simple components. e=mc2 is a remarkably simple formula that forms the theory of relativity, one of the most revolutionary ideas ever.
  • Simple code does not necessarily indicate simple behavior. Recursion, multithreading, and composition can let you build applications out of simple building blocks with amazingly complex behavior.
  • Writing simple code does not mean taking the easy way out. Cutting and pasting is often the fastest way to write a new method, but it's not always the simplest solution, and rarely the best solution. Simple code is clean, with little replication.
  • A simple process is not an undisciplined process. Extreme programming is a process that embraces simplicity, and it's quite rigorous in many ways. You must code all of your test cases before writing your code; you must integrate every day; and you must make hard decisions on project scope in order to keep to your schedule.
Simple code is clean and beautiful. Learn to seek simplicity, and you'll step over the line from engineer to artist. Consider the evolution of a typical guitar player. Beginners aspire to play just about anything that they can master. Intermediate players learn to cram more notes and complex rhythms into ever-decreasing spaces. If you've ever heard one of the great blues players, you know that those players have mastered one more skill—they learn what
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Process and Simplicity
Kent Beck, the father of XP, says "Pick the simplest thing that will work." Building the simplest house or making the simplest of car repairs is difficult without the right tools and process. Building great software is no different. If you want to build simple software, you've got to strip all the extraneous junk out of your process that clutters your mind, your motivations, and your code. As you've seen in Chapter 1, I don't think that most development shops are moving in the right direction. The same forces that bloat frameworks, languages, and tools can also convolute the everyday development process:
Overkill
Heavy-duty processes used in the mainstream are designed for the most difficult problems. For the most part, UML diagrams such as sequence diagrams, class diagrams, and the like provide more harm than value. To me, UML belongs in books, on white boards, and possibly in the classroom, but rarely in design documents.
Complexity
When you do need to add tools like UML, keep it simple. A box with the class name and the two most important methods is often more useful than a multisymbol mish-mash with every UML bell and whistle embedded.
Indirection
It's hard to keep those phone book-sized requirements documents in sync with the code. It's even harder to keep that 350-class set of UML diagrams up to date. I've got another, more effective rule for synchronizing documents: code always wins. Code is the primary artifact that you'll deliver.
Rigidity
Those that sell methodology also often sell dogma. It doesn't matter whether you're going with a high-end process like Rational Unified Process (RUP) or a lower-intervention process like XP.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Your Safety Net
Agile methods like XP have introduced new practices and philosophies into mainstream development that will change the way that you code forever. One of the practices that is gaining rapidly in popularity is unit test automation. Remember that refactoring is a foundation. In order to refactor, you've got to test all of the classes related to your refactored class. That's a pain. That's why unit testing is a fundamental building block for simplicity, because it provides the confidence to refactor, which enables simplicity, like the pyramid in Figure 2-3.
Figure 2-3: Automated unit tests provide the foundation for simplicity
You can see how these concepts build on one another. You're free to choose simple concepts because you can refactor if the simple solution is insufficient. You're free to refactor because you'll have a safety net. Automated tests provide that net.
Chances are good that you're already using JUnit. If so, you can skip ahead to the next section. If you're not using JUnit, you need to be. JUnit is an automated testing framework that lets you build simple tests. You can then execute each test as part of the build process, so you know immediately when something breaks. At first, most developers resist unit testing because it seems like lots of extra work for very little benefit. They dig in their heels (like another Dr. Seuss character, saying "I do not like green eggs and ham"). I'm going to play the part of Sam I Am, the green-eggs-and-ham pusher, and insist that you give it a try. Automated unit testing is foundational:
JUnit testing lets you run every test, with every build.
Further, you can create automated tests with no more effort than it takes to build a single test (after you've done a little set up). When something breaks, you know immediately. You run tests more often and have built-in regression tests to catch errors.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
Simplicity is perhaps the core value of Java development. It makes you more productive, improves the readability of your code, reduces bugs, and makes it easier to layer core concepts. It's a concept that does not stand alone. You must consider the foundation that you're using—whether it's a framework, design pattern, or your own code. You've also got to worry about your development process, because complex development processes frequently lead to complex code.
Finally, you've got to embrace the practices of refactoring and testing to have the freedom that leads to simplicity. One XP principle is to try the simplest thing that will work. This adage works well within XP because if you're wrong, you can refactor. Since you started with a simple solution, you're not going to lose much. Refactoring, through, can be dangerous, so you need a safety net. Automated unit tests provide that safety net.
In the next chapter, I'll introduce the principle "Do one thing, and do it well." You'll learn how to focus the goals of each part of your system. As you'd probably expect, I'll also spend some time talking about reducing coupling.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: Do One Thing, and Do It Well
There's only one way to eat an elephant: a bite at a time. That's also the best way to code. Each bite of clear, simple Java code must have a single purpose. The best Java programmers keep a maniacal focus on a single problem at a time and go to extraordinary lengths to maintain that focus. If you want to improve, emulate them.
I'm a whitewater kayaker. For a long time, I walked around every serious rapid that I faced. I could see how to run a one-shot waterfall or drop, but I couldn't get my head around the linked moves that would take me safely through Humpty-Dumpty on the Little River, or .25-mile Pine Creek on the Arkansas. A light clicked on for me on the Watauga River in Tennessee. I learned that I just couldn't bomb down continuous Class IV rapids with a preconceived set of moves in my head. Instead, I needed to find the natural break points within the rapids, and run many little ones. I learned to read the river and find the natural resting places within the whole. Then I could conquer one section, set up, and attack the next section. When I approached the problems this way, I received unexpected benefits. Coding, to me, is similar:
  • It's usually easier to clearly define a piece of a big problem than the whole. We all tend to get overwhelmed by large problems, but not as much by many smaller ones. Our brains just work that way, whether we're on a river or behind a keyboard.
  • When things go wrong, it's easier to adjust or adapt if your plan is segmented. Plans change; it's harder to change a grand, sweeping plan than several smaller ones.
  • You can protect yourself from disaster. On the river, I now plan for safety one small section at a time. While coding, we must build test cases that identify problems quickly in each logical section.
  • You can better reuse techniques and code. On the river, I learn new moves that I can use elsewhere. Instead of frantically paddling through a section, I use a draw stroke to avoid one rock, an aggressive brace and sweep to punch a hydraulic, and so on. In code, I build collateral and learn techniques to solve smaller, more general problems. Each design pattern that you learn in context is worth any 20 that you read about.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Understanding the Problem
Communication is a huge part of programming. Writing better Java won't do you any good if you're building the wrong thing: you must understand before you can code. As you advance in your programming career, you'll find that more and more of your job demands effective communication. It doesn't really matter where your requirements come from—your team lead, an analyst, or the customer. In each case, your first job is to accurately gather each requirement and focus your customer on what you can reasonably achieve with your resources at hand. Don't plow your customer under with enormous requirements and arcane design documents. If the customers can't understand your documents, they can't focus on the real issues. Like your code, keep your requirements simple and clear.
If you don't like communication, I've got news for you: things are going to get worse before they get better. In a global economy, you've got to be efficient. Increasingly, that means developers must handle more and more of the software development process. Out of college, I worked for the largest software company in the world. We had more testers than coders on a project (often, by a factor of 3 to 1), and teams of 10 planners supporting 40 developers. The overall effort was 200 developers strong. Now, that project might use 10 developers and half the original timeframe to develop the same piece of software. Since the development of better automation and unit testing, each modern developer must shoulder more of the testing load. Coders also do more design work and planning than ever before. However, experience shows that many of those developers are not equipped to handle many of the increased planning and analysis roles that they face.
This book would not do its readers justice if we didn't talk about dealing with change from a process perspective. If you're one of those shops that tries to do more with less, you need to do two things in the planning process: first, weed out software requirements that don't contribute much to the final project, and second, weed out unnecessary work that supplements traditional development but does not contribute much to the overall content or quality of your code.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Distilling the Problem
Virtuosos in any profession have a common gift: they can distill a problem to its basic components. In physics, Einstein identified and captured many complex relationships in a simple equation, e=mc2. Beethoven captured and repeated a motif consisting of four notes in his fifth symphony that's endured for centuries. Programming demands the same focus. You've got to take a set of requirements, identify the essential elements, strip away everything that doesn't belong, and finally break down and solve the problem.
To improve your programming, you don't have to live in a cave, reading about a design pattern that covers every possibility. You don't need to know the latest, hottest framework. You've just got to focus on the right problem, distill it to the basics, and hammer out the simplest solution that will work. In this section, I'm going to take a set of requirements, distill them, and turn them into code.
Let's take a simple example. Say that you're building an ATM. Your job is to build the support for an account. In keeping with Agile programming practices, you've decided to keep a simple set of requirements in a table. You'll record the requirement number, a brief description, a size (timeline), and choose a programmer. Your team is small and your cycles are short, so that's all that you think you'll need. Table 3-1 shows the basic requirements.
Table 3-1: Requirements for account project
Number
Description
Size (hours)
Assigned
1
Keep a balance and account number
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Layering Your Architecture
This book is about building complex code in a simple way. The principle in this chapter, "Do one thing, and do it well," may seem like it argues against building complex software. But it simply means each major piece focuses on a single aspect of the overall solution.
You can organize an application in layers, so that you'll only have to deal with small, focused chunks of the application at any given point of time. In this section, I'll talk about the anatomy of a layer, the interfaces between layers, and the typical layers that you're likely to find in better Java applications. Before I start piling on generic information about how you should build layers, here's what you should not do:
  • Don't bite off too much in any given layer. Your layers should have a simple purpose, and they should be easy to digest at one sitting. If your layers are too fat, they'll be too hard to test, maintain, and understand.
  • Don't add a layer because you read about it in a book. In most cases, you should add new layers and design patterns based on real, experienced need, not assumed need.
  • If you're testing as you go, your tests will dictate some level of layering. Don't resist. Your tests mirror usage patterns in many ways, and they'll make your code easier to reuse and decouple as the need arises.
  • Pay attention to names. Think of each independent layer as a library of services, even if there's just one client at the moment. If you misname your services, your layer will be misused. If a name becomes a problem, change it. If it's too hard to change names, get a tool that lets you do so. Intellij's IDEA was used to build some of the software in this book, and its refactoring tools are extremely useful.
All developers layer; some just do so more effectively and intentionally. In the last section, you probably noticed that I threw some requirements out to handle later. I did so because the postponed requirements were natural layers for the emerging architecture. I explicitly defined layers for the business domain model and the data access object. I implicitly defined a layer for the user interface, a potential façade, and security. Let's look at the anatomy of a layer.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Refactoring to Reduce Coupling
You may start with a cleanly defined design and you may layer your design, as we've discussed, so that each layer does one autonomous job. You may have coupling only at the appropriate places. But if you don't try to maintain that design, it won't last. Your code will naturally move toward tighter coupling unless you fight that tendency. In the last part of this chapter, we review some of the types of coupling, and how to avoid them. The benefits of looser coupling include:
  • Decoupling protects major subsystems of your architecture from each other. If the coupling between your model and view are low, then changes in one will not severely affect the other.
  • Loosely coupled code is easier to read. If you couple your business domain model to your persistence framework, then you need to understand both to read your domain model. Loosen the coupling and you can read your domain model unencumbered.
  • Decoupling can improve reuse. It's harder to reuse big blobs of software, just like it's harder to reuse a full engine than a spark plug.
Keep in mind that some coupling is natural. You've got to have some degree of coupling to do anything at all. Coupling gets out of hand when things that don't belong together are bound together. Your goal should be to avoid accidental coupling—you want any coupling in your application to be intentional and useful.
Also, keep in mind that decoupling often comes at a price. You can add JMS queues and XML messages between every class, but you'll work harder and your application will be dog slow. Decoupling becomes much more important between major subsystems and layers of your architecture.
Most of the code written today has many relationships, and most of those relationships are tightly coupled. Anything from a method call to the use of a common variable increases your coupling. Like I said earlier, that's not inherently bad. You just want to keep it intentional, and keep your coupling confined to an area of the architecture.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
This chapter makes only one point: great software maintains focus on one task. To focus software, sharpen your ability to collect requirements and control your customers. If you're not careful, scope creep can confuse the basic theme of your software. When you've got a more complex problem, break each fundamental theme into a layer, or subsystem. In general, common layers are always evolving for Java technologies. Many of the accepted practices are sound, but others are suspect. Better layers share a common purpose and an effective interface.
Once you've designed effectively layered software and built clean software with a distilled purpose, maintain your clarity of purpose. To keep software focused on a central theme, you'll need to frequently refactor to loosen the coupling around tightly coupled components. Loose coupling is desirable at a lower level, and you can control it by testing and refactoring with techniques like interfaces. Also, pay attention to coupling at a higher level, so that each major subsystem is as isolated as possible. You'll improve reuse and isolate one subsystem from changes in others. In the next chapter, we'll discuss how to take the some extreme steps to reduce coupling between business domain models and services through increased transparency.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: Strive for Transparency
In the Lord of the Rings trilogy, the fate of Frodo Baggins is tied to the ring of power. The ring provides incredible power, but at a terrible cost. In the end, it invades his sleep, every hour of consciousness, and even his relationships. He is so possessed by the ring that he cannot drop it, although it is consuming him. I've suffered projects where our frameworks felt a little too much like that ring. In the beginning, the power blinds us, and near the end, it invades the core of our being, from the design philosophies all the way to the thoughts and mood of the whole team. In fact, I've even helped to build, and sell, such a framework, a demon disguised by the false name of CORBA. I'm not alone. I've been approached to rescue many an application from a cloud of doom, whether it's CORBA, VisualBasic, EJB, or even database stored procedures and user-defined functions.
In Chapter 3, our goal was to focus our software efforts on a central task. There weren't any earth-shattering techniques. You probably already do some kind of layering, and at least understand the value of decoupling. In this chapter, you'll see techniques to take the power of decoupling to the next level. In fact, it's often important enough to decouple critical layers like your domain model from all other services from the very beginning. You're looking for transparency, and this chapter introduces the techniques that you need to get there.
For most of this chapter, I'm going to explore the relationship between a service and a model. Your ultimate goal is to build a layer that's completely independent from the services that it uses. In particular, you want to keep all peripheral systems out of the domain model—persistence, transactions, security—everything. Why should the business domain model get such special treatment?
  • Business models tend to change rapidly. Transparency lets you limit the changes to business logic.
  • With transparency, you can limit changes to other parts of the system when your model changes.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Benefits of Transparency
For most of this chapter, I'm going to explore the relationship between a service and a model. Your ultimate goal is to build a layer that's completely independent from the services that it uses. In particular, you want to keep all peripheral systems out of the domain model—persistence, transactions, security—everything. Why should the business domain model get such special treatment?
  • Business models tend to change rapidly. Transparency lets you limit the changes to business logic.
  • With transparency, you can limit changes to other parts of the system when your model changes.
  • You can understand and maintain transparent models much more quickly than solutions with tighter coupling.
  • By separating concerns of your layers, you can focus business, persistence, and security experts in the areas that make them the most productive.
You can also build completely generic services that know nothing in advance about the structure of your model. For example, a persistence service can save any generic Java object; a security service needs no additional code, but is based on configuration instead; a façade gets the capability to make a series of steps transactional just by adding a POJO to a container; a serialization service can turn any object into XML without knowing its structure in advance.
The core techniques in this chapter—reflection, code injection and other code generators—pack a punch, but they also add complexity and weight to your applications. My hope is that with a little supporting theory on your side, you'll be able to use these techniques to pry that ring out of your hand.
To be sure, none of these ideas are new, but I don't believe that they've received the weight that they deserve in the Java mainstream. I'll first talk about moving the control centers of the application to the appropriate place. Then, I'll give an overview of the tools that limit transparency, and wrap up with a few recommended tools to achieve it: code generation, reflection, and byte code enhancement, with an emphasis on reflection. If you're not used to coding this way, you'll find that it's going to warp your mind a little. Take heart. You've probably seen these techniques before, though you may need to rearrange the ideas a little. In the end, you'll find the ideas that percolated in the center of Smalltalk and around Java's perimeter have the power that you want, and even need.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Who's in Control?
Close to the center of my universe are my two little girls. One Christmas, we splurged a little and traded in our out-of-date 19-inch TV for a much larger model. We set the new television up. A little later, I looked around the room: one of my daughters was staring with a blank, passive face at frantic Disney images on the TV. The other kid had the cardboard box. With her mother, she had carved out doors and a window, and was actively jumping and dancing around the passive box. The contrast was striking. On one side, I saw an active toy, and a passive kid; on the other side, a passive toy and an active kid. Since then, I've repeated the experiment, albeit more intentionally. I've filled my house with passive toys that let the kids actively build, imagine, create, or act (at least, when I can pry them away from the TV).
Modern programming is undergoing a similar transition from active to passive domain models. Figure 4-1 shows the organization of classic services in a client-server application. Designers put services on the bottom, so their clients could build applications that use the services directly. Essentially, active business logic invoked services as needed. The passive services presented an interface, and waited idle until invoked. Early programmers found it easy to build applications with active business layers. When they needed a transaction, they called a function like BeginWork. When they needed data, they asked for it directly from the database. Easy development gave way to complex maintenance and foiled attempts at extension, because the architecture muddied and entangled concerns between layers.
Figure 4-1: This client-server model lets service logic intrude into the business logic, increasing coupling and complicating code
The problem at the core of this type of design is the commingling of business
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Alternatives to Transparency
Most people use the term transparent in a very specific sense, referring to code that does a job without explicitly revealing the details of that job. Distributed code with location transparency does not explicitly refer to the location of other machines on the network.
Consider a specific example. Persistence frameworks let you save Java objects. If you don't have to build in any support to get those benefits, then you've got transparent persistence. You'd think that you've either got transparency or you don't, but unfortunately, it's not always black or white. You may need to make some minor compromises:
  • Your code may be transparent, but you may have to deal with minor restrictions. For example, some frameworks use JavaBeans API, and require getters and setters on each field (such as earlier versions of Hibernate).
  • You may have to deal with major restrictions. Some frameworks don't support threading or inheritance, like EJB CMP.
  • You may need to add special comments to your code. XDoclet relieves some limitations in other frameworks through code generation, but forces you to maintain specific comments in your code.
  • You may have to make minor code changes, like supporting an interface or inheriting from a class.
  • Your framework may generate your code, but you may not be able to modify that code and still support regeneration, like many IDE wizards.
  • You may need to change the build process, such as in frameworks with code generation like Coco Base, or frameworks like JDO with byte code enhancement.
Some of these restrictions are minor, but some are severe. Before I dig into techniques that promote transparency, you should know about other available techniques, and their possible limitations.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Reflection
The most accessible way to build a service that depends on model data is runtime reflection. I don't know why Java developers never have embraced runtime reflection the way that other languages have. It tends to be used by tool developers and framework providers but not general application programmers. You don't have to use reflection everywhere, nor should you try. Instead, apply a little reflection where it can have a tremendous impact. Here are some things to keep in mind:
General needs versus specific needs
When you need to access an object's features in a general way, use reflection. For example, if you're moving the color field from an object to a buffer for transport, and you don't ever use the field as a color, consider reflection for the task, in order to reduce coupling. If you're reading the color and setting other objects to the same color, direct property accesses might be best.
Delaying decisions
If you don't know the name of a method or class until runtime, consider reflection. If you already know the name, there's no reason to delay the decision—a simple method call is a better choice. For example, through configuration, you can frequently decouple code and delay decisions until runtime. Reflection gives you a tool to help this happen.
Bear in mind that although the performance of reflection has improved in recent years, postponing binding decisions until runtime has a performance cost. Jumping to a specifically named method or property is much faster than going through the reflection API by a factor of two or more. Reflection offers great power, but be judicious.
The Java reflection API lets you access all of the elements that make up a class at runtime without requiring source code or any other advanced knowledge of the classes. Using reflection, you can:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Injecting Code
To get better transparency, you can always automatically generate code and add it to the model. To date, most frameworks use this approach. Of course, since most Java developers use Ant to build their projects, adding a simple code enhancer to your applications is relatively easy to do with little intrusion on your build process. You can use code enhancement in two ways:
Source code enhancement
This technique uses a program to read through your source code and make additions in the necessary places. For example, to make code transparent with respect to a performance tool that does performance profiling, you might run a precompiler program that injects code that takes a timestamp any time you enter or exit a method you want to measure.
Byte code enhancement
Since Java programs compile to a standard compiled form called byte code, you can inject byte code to add services and still maintain transparency. For example, most JDO implementations use a byte code enhancer.
Often, when you inject code, you're actually injecting methods that perform the work of your intended service, as with the source code enhancer in Figure 4-6. Source code enhancement takes a class as an input and then generates code, typically method calls, to inject service capabilities into code, completely preserving transparency in the original class.
Figure 4-6: This source code enhancer addslogging to MyClass
JDO enhancers work this way: you create a class that's transparent with respect to persistence. The job of the JDO enhancer is to implement the PersistenceCapible interface. In order to make the class persistent, let your build process run it through a JDO enhancer. (Some of these use source code enhancement but most use byte code enhancement.) The enhanced class then calls the JDO framework to actually implement persistence. Some aspect-oriented programming frameworks use byte code enhancement, as well. The technique has many benefits:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Generating Code
As you've probably noticed, many Java frameworks require a whole lot of tedious, redundant syntax. In his book Refactoring (Addison-Wesley), Martin Fowler calls such a design a "code smell." Since Java developers are a lazy and creative lot, they seek ways to automatically generate repeated bits of code. Further, they think of ingenious ways to configure their code generation engines. Take an EJB application, for example. In order to create the persistent model with a remote interface, you'll need to create at least seven files: the home object for lifecycle support, a local that serves as a proxy, the interface, implementation, primary key, deployment descriptor, and schema. With code generation tools like XDoclet, you can automatically generate at least five of the seven, and often six of the seven. You create an XDoclet by instrumenting your code with simple JavaDoc comments. While this technique doesn't make your code completely transparent with respect to persistence, it certainly makes it more transparent.
While novice and intermediate Java developers see code generation as black magic, it's really quite simple. If you've ever used a mail merge program, you know how it works. You create a working piece of code. Then you mark the areas of the code that vary from instance to instance. Together, these form your template. Next, you provide data to fill in the variables. Like a mail merger, the code generator takes your template, fills in the blanks, and generates working code, as in Figure 4-7.
Figure 4-7: Code generation works by combining a template with data
Figure 4-7 shows the general concept, although it simplifies the problem in several ways. You can generate multiple targets. You can also generate code with complex structures, such as repeated or conditional blocks. In general, if you can describe the patterns in your code and clearly define areas of duplication, you can probably find or build something to generate it. There are several types of generation strategies:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Advanced Topics
Now you've seen three tools for achieving transparency. If you're anything like my clients, you're probably wondering which is best. I'm going to carve that decision into three pieces:
  • If I'm building a transparent service myself, I prefer reflection. I'd simply prefer to call a library than build a code generator or byte code injector. I prefer to have business logic within my domain model (instead of just data holders), and that eliminates code generation. Though the performance is doubtlessly superior, byte code generation is too difficult and risky for most small or inexperienced IT shops.
  • If I'm buying a tool or framework, I like the idea of byte code enhancement. I like that you pay much of your performance penalty at build time instead of runtime and I like that after the build, I don't have to worry about the service. With tools like JDO, I've rarely had instances where byte code enhancement made things difficult for me to debug, and I've always been impressed with the flexibility of byte code generation over reflection. As a case in point, after coming down hard on JDO vendors in their marketing literature, Hibernate in fact added a byte code enhancement library, called CGLIB, to improve certain aspects (such as lazy loading).
  • I don't mind code generators, but I don't lean on them for transparency. In general, better techniques get the same benefits without some of the drawbacks mentioned earlier in this chapter.
If you're gung-ho about transparency, keep an eye on a couple of evolving debates. The first is the concept of coarse- and fine-grained services. The second is the future of programming techniques that may enhance your experience.
Nearly all applications support two types of services: coarse- and fine-grained. You may decide that it makes perfect sense to attach all services to the same point. Be wary, though. Many early EJB applications used that design, God rest their souls. Your problem is two-fold. First, if you present an interface, your users may use it whether it's a good idea or not. Second, different services have different performance requirements.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
If you're trying to decouple that service from your model and you feel like you're standing half-dressed after pulling a single thread a little too far, take heart. If you put the effort into building transparency into your application, you're likely to get where you intended to go, fully dressed and on time.
Some other decoupling techniques don't go far enough. Inheriting a service leads to awkward models that are tough to extend in other ways. Hardwiring services has a place, but it starts to be limiting as an application grows in scope. New programming models such as AOP may help you some day, but others (like heavyweight invasive containers) can kill you.
Instead, if you've got an exceptional need to decouple a service from your model, strive for transparency. Effective frameworks seem to be divided across three camps. All have relative strengths and weaknesses.
  • Reflection is the tool of choice if you're building a lightweight transparent service. It's relatively easy to use and doesn't require any changes to your build process. The downside is performance but if you don't overuse it, reflection is fast enough for many applications.
  • Enhancement techniques directly modify the byte code in your application to perform the appropriate task. They do change the build process, and may theoretically be difficult