Better Faster Lighter Java [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Better Faster Lighter Java [Electronic resources] - نسخه متنی

Justin Gehtland; Bruce A. Tate

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








1.1 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
randomlypowerful 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-Frameworks


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.

Collaboration can increase bloat. Whenever you get multiple agendas
driving a software vision, you get software that supports multiple
agendas, often with unintended consequences. That's
why we have two dramatically different types of EJB. The process
satisfied two dramatically different agendas.


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 programming

Figure 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: implementation


    package 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:

[1] This class implements the EJB interface, and
you've got to use it in the context of an EJB
container. The code must be used inside a container. In fact, you can
use it only within an EJB container. You cannot run the code with
other types of containers.

[2] You see several lifecycle methods that have nothing to do
with our business function of counting:
ejbActivate, ejbPassivate,
ejbStore, ejbLoad,
ejbRemove, setEntityContext,
and unsetEntityContext.

[3] Unfortunately, I've had to tuck all of
the application logic away into a corner. If a reader of this
application did not know EJB, he'd be hard-pressed
to understand exactly what this class was designed to do.


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 interface


package 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 interface


package 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 Progress


Developers 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 bloat

If 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!


Table 1-1. Zip file size for standard edition Java developer kit in Version 1.1 and Version 1.4

JDK version, for Windows


Zip file size


JDK 1.1


3.7 MB


J2SE 1.2


20.3 MB


J2SE 1.3


33.2 MB


J2SE1.4


38.0 MB

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:

Some of the growth is occurring


in
the standard libraries. If the bloat were purely in add-on libraries,
then you could perhaps avoid it by choosing not to install the
additional libraries. But you can't dodge the
standard libraries. That means that your resource requirements will
increase.

Java is harder to learn. Early


versions of Java allowed most programmers to
pick up a few books, or go to class for a week. Today, the learning
curve is steeper for all but the most basic tasks. While the steep
curve may not directly affect you, it does affect your project teams
and the cost of developers.

It's harder to find what you need. Since the
libraries continue to grow, you need to wade through much more data
to find the classes and methods that you need to do your job.

You need to make more decisions. As alternatives appear in the basic
Java toolkits (and often in open source projects),
you've got to make more decisions between many tools
that can do similar jobs. You must also learn alternatives to
deprecated classes and methods.

You can't fully ignore old features: people still
use deprecated methods. How many Vectors have you
seen in the past couple of years?


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 Forces


To 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 expandand
extend the inevitable bloat.


1.1.4 Misuse


Nothing 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:

Framework overkill


I've seen a departmental calendar built with
Enterprise JavaBeans. I've also seen tiny programs
use XML for a two-line configuration file.


Design patterns


These days, it's almost too easy to use a design
pattern. When you trade power for simplicity too many times, you get
bloat.


Sloppy reuse


If you try to stuff a round peg in a square hole,
you'll have to adapt the hole or the peg. Too many
adaptations will often lead to bloat. Cut-and-paste programming also
leads to bloat.


Poor process


Like fungus in a college refrigerator, bloat best grows in dark,
isolated places. Isolated code with no reviews and one owner lets
bloat thrive unchecked.



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.


/ 111