< Day Day Up > |
1.1 Bloat DriversI'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. 1.1.1 Enterprise Mega-FrameworksJava 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:
You can almost watch each new enterprise framework succumb to the bloat, like chickens being fattened for market. In its first incarnation, XML was slightly tedious, but it provided tremendous power. In truth, XML in its first iteration did almost everything that most developers needed it to. With the additions of XML Schema and the increased use of namespaces, XML is dramatically more cumbersome than ever before. True, Schema and namespaces make it easier to manage and merge massive types. Unfortunately, once-simple web services are taking a similar path. But none of those frameworks approach the reputation that Enterprise JavaBeans (EJB) has achieved for bloat. EJB container-managed persistence (CMP) is the poster child for tight coupling, obscure development models, integrated concerns, and sheer weight that are all characteristic of the bloat (Figure 1-1). Figure 1-1. In theory, EJB's beans simplify enterprise programmingFigure 1-1 shows the EJB container-based architecture. Beans plug into a container that provides services. The premise is sound: you'd like to use a set of system services like persistence, distribution, security, and transactional integrity. The EJB is a bean that snaps into the container, which implements the set of services that the bean will use. Within the bean, the developer is free to focus on business concerns in the bean. My favorite childhood story was The Cat in the Hat by Dr. Seuss, who should have been a programmer. I loved the game called "Up, up, with the fish," in which the Cat tries to keep too many things in the air at once. As an EJB programmer, it's not quite as funny, because you're the one doing the juggling. Consider this very simple example in Example 1-1. I want a simple counter, and I want it to be persistent. Now, I'll play the Cat, and climb up on the ball to lob the first toy into the air. Example 1-1. Counter example: implementationpackage com.betterjava.ejbcounter; import javax.ejb.*; import java.rmi.*; /** * CMP bean that counts */ [1] public abstract class Counter implements EntityBean{ private EntityContext context = null; public abstract Long getID( ); public abstract void setID(Long id); public abstract int getCount( ); public abstract void setCount(int count); Ͽ public abstract Object ejbCreate(Long id, int count); throws CreateException { setId(id); setCount(count); return null; } public void ejbPostCreate(Long id, int count) throws CreateException { } public void setEntityContext(EntityContext c) { context = c; } public void unsetEntityContext( ) { context = null; } public void ejbRemove( ) throws RemoveException { } public void ejbActivate( ) { } public void ejbPassivate( ) { } public void ejbStore( ) { } public void ejbLoad( ) { } [3] public void increment( ) { int i=getCount( ); i++; setCount(i); } public void clear( ) { setCount(0); } } The first file, called the bean, handles the implementation. Note that this class has the only business logic that you will find in the whole counter application. It accesses two member variables through getters and setters, the counter value and ID, which will both be persistent. It's also got two other methods, called clear and increment, that reset and increment the counter, respectively. For such a simple class, we've got an amazing amount of clutter. You can see the invasive nature of EJB right from the start:
I'm not going to talk about the limitations of container-managed persistence. If you're still typing along, you've got four classes to go. As the Cat said, "But that is not all, no that is not all." Example 1-2 shows the next piece of our EJB counter: the local interface. Example 1-2. Local interfacepackage com.betterjava.ejbcounter; import javax.ejb.*; /** * Local interface to the Counter EJB. */ public interface CounterLocal extends EJBLocalObject { public abstract Long getID( ); public abstract void setID(Long); public abstract int getCount( ); public abstract void setCount(int count); } This is the interface, and it is used as a template for code generation. Things started badly, and they're deteriorating. You're tightly coupling the interface to EJBLocalObject. You are also dealing with increasing repetition. Notice that I've had to repeat all of my implementation's accessors, verbatim, in the interface class. This example shows just one instance of the mind-boggling repetition that plagues EJB. To effectively use EJB, you simply must use a tool or framework that shields you from the repetition, like XDoclet, which generates code from documentation comments in the code. If you're a pure command-line programmer, that's invasive. But, "`Have no fear,' said the Cat." Let's push onward to Example 1-3. Example 1-3. LocalHome interfacepackage com.betterjava.ejbcounter; import javax.ejb.*; import java.rmi.*; import java.util.*; /** * Home interface to the local Counter EJB. */ public interface CounterLocalHome extends EJBLocalHome { public Collection findAll( ) throws FinderException; public CounterLocal findByPrimaryKey(Long id) throws FinderException; public CounterLocal create(Long id, int count) throws CreateException; } In Example 1-3, you find the methods that support the container's management of our persistent object. Keep in mind that this class is a generic, standalone persistent class, with no special requirements for construction, destruction, or specialized queries. Though you aren't building any specialized behavior at all, you must still create a default local home interface that builds finder methods and templates for the lifecycle of the bean, like creation and destruction. At this point, I'm going to trust that you've gotten the message. I'll omit the painful deployment descriptor that has configuration and mapping details and the primary key object. I'm also not going to include a data transfer object (DTO), though for well-documented reasons, you're not likely to get acceptable performance without one. Dr. Seuss sums it up nicely: "And this mess is so big and so deep and so tall, we cannot pick it up. There is no way at all." You'd be hard-pressed to find a persistence framework with a more invasive footprint. Keep in mind that every persistent class requires the same handful of support interfaces, deployment descriptors, and classes. With all of this cumbersome, awkward goo, things get dicey. Some Cats have enough dexterity to keep all of those toys in the air. Most don't. 1.1.2 ProgressDevelopers do not want their programming languages to stay still. They want them to be enhanced and improved over time; so, we must continually add. Yet language vendors and standards boards can't simply remove older interfaces. In order to be successful, languages must maintain backwards compatibility. As a result, additions are not usually balanced with subtractions (Figure 1-2). That's a foolproof recipe for bloat. Figure 1-2. Backwards compatibility with progress leads to bloatIf you'd like to see an example of this principle in action, look no further than the deprecated classes and methods in Java. Deprecated literally means "to disapprove of strongly," or "to desire the removal of." In Java, Sun warns against the use of deprecated classes and methods, because they may be removed in some future release. I assume that they are defining either remove or future very loosely, because deprecated methods never disappear. In fact, if you look at the AWT presentation library for Java, you'll find many methods that have been deprecated since Version 1.1, over a half a decade ago. You can also look at the other side of the equation. The next few versions of Java are literally packed with new features. If you're wondering about the impact of these changes on the overall size of the Java runtimes, then you're asking the right questions. Let's take a very basic metric: how big was the Zip file for the Windows version of the standard edition SDK? Table 1-1 shows the story. In Version 1.1, you would have to download just under 3.7 megabytes. That number has grown to 38 megabytes for JDK 1.4!
You may ask, so what? Computers are getting faster, and Java is doing more for me than ever before. It may seem like you've got a free ride, but the ever-growing framework will cost you, and others:
Platforms are not immune to the bloat. That's a fact of life that's beyond your control. My point is not to add needless anxiety to your life, but to point out the extent of the problems caused by the bloat. 1.1.3 Economic ForcesTo be more specific, success drives bloat. The marketplace dictates behavior. Microsoft does not upgrade their operating systems to please us, or to solve our problems. They do so to make money. In the same way, commercial drivers will continue to exert pressure on Java to expand, so you'll buy Java products and align yourself with their vision. Beyond license fees, Sun does not make money directly from Java, but it's far from a purely altruistic venture. The Java brand improves Sun's credibility, so they sell more hardware, software, and services. Market leaders in the software industry cannot stay still. They must prompt users to upgrade, and attract new customers. Most vendors respond to these challenges by adding to their feature set. For just one example, try installing Microsoft Office. Check out the size of the Word application. Though most users do little more than compose memos and email, Word has grown to near-Biblical proportions. Word has its own simple spreadsheet, a graphics program, and even web publishing built in. Most Word users have noticed few substantive changes over the years. To me, the last life-changing enhancements in Word were the real-time spelling checker and change tracking. Upgrade revenue and the needs of the few are definitely driving Word development today. Keep in mind that I'm an author, and spend way too much time in that application. Of course, we can't blame Microsoft. They're trying to milk a cash cow, just like everyone else. Yet, like many customers, I would be much happier with a cheaper word processor that started faster, responded faster, and crashed less. Within the Java industry, BEA is an interesting illustration of this phenomenon. To this point, BEA has built a strong reputation by delivering an outstanding application server. From 2001 to the present, BEA and IBM have been fighting a fierce battle to be the market-leading J2EE application server. IBM increased their WebSphere brand to include everything from their traditional middleware (the layer of software between applications and the operating system) to extensions used to build turnkey e-commerce sites and portals. Two minor competing products, JBoss and Oracle9iAS, were starting to eat away at BEA's low-end market share. Both of these products were inexpensive. Oracle priced their product aggressively for users of their database, and JBoss was an open source project, so BEA was under tremendous pressure to build more value into their product and stay competitive. They responded by extending their server to enterprise solutions for building portal software, messaging middleware, and business integration. They also started a number of other initiatives in the areas of data (Liquid Data), user interface development (NetUI), and simplified application development (WorkBench). Building a great J2EE application server is simply not enough for BEA any more. They, too, must expand—and extend the inevitable bloat. 1.1.4 MisuseNothing drives bloat more than misuse. If you go to Daddy's toolkit and borrow his cool pipe wrench when you need to drive a nail, something's going to go awry. The book Antipatterns, by William J. Brown, et al. (Wiley & Sons), refers to this problem as the golden hammer. When you've got a golden hammer, everything starts to look like a nail . Misuse comes in many forms:
Many developers wear golden hammers as a badge of honor. Reaching for the wrong tool for the job is nearly a rite of passage in some of the places that I've worked. It's a practice that may save a few minutes in the short term, but it will cost you in the end. |
< Day Day Up > |