Download Effective Tools and Techniques for Java Developers

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts
no text concepts found
Transcript
Effective Tools and Techniques
for Java Developers
InformIT is the online presence of the family of information technology
publishers and brands of Pearson Education, the world’s largest educational
publisher. InformIT is home to the leading technology publishing imprints
Addison-Wesley Professional, Cisco Press, ExamCram, IBM Press, Prentice
Hall Professional, QUE, and Sams. Here you will gain access to trusted and
quality content and resources from the authors, creators, innovators, and
leaders of technology. Whether you’re looking for a book on a new
technology, a helpful article, timely newsletters or access to the Safari Books
Online digital library, InformIT has a solution for you.
informit.com/java
1
This paper includes a collection of excerpts from titles published by Pearson Education
including Addison-Wesley and Prentice Hall and pointers to resources for Java developers
and programmers at all levels. The contents include:
• Creating and Destroying Objects by Joshua Bloch
• Clean Code by Robert C. Martin
• Behavior by Kent Beck
• Tag Libraries: The Basics by Marty Hall, Larry Brown, and Yaakov Chaikin
Recommended Resources
Copyright © 2008 by Pearson Education, Inc. All rights reserved. No part of this
publication may be reproduced, stored in a retrieval system, or transmitted, in any form,
or by any means, electronic, mechanical, photocopying, recording, or otherwise, without
the prior consent of the publisher.
For information on obtaining permission for use of material from this work, please submit
a written request to:
Pearson Education, Inc.
Rights and Contracts Department
75 Arlington Street, Suite 300
Boston, MA 02116
Fax: (617) 848-7047
informit.com/java
2
Contents
Article 1: Effective Java
by Josh Bloch
Article 2: Clean Code
by Robert C. Martin
Article 3: Implementation Patterns
by Kent Beck
Article 4: Core Servlets
by Marty Hall, Larry Brown, and Yaakov Chaikin
Recommended Resources
informit.com/java
3
Article 1—Effective Java
Joshua Bloch is the Chief Java Architect at Google. Previously he was a Distinguished Engineer at Sun Microsystems and a Senior Systems Designer at
Transarc. He led the design and implementation of numerous Java platform features, including the JDK 5.0 language enhancements and the Java Collections
Framework. He holds a Ph.D. in Computer Science from Carnegie-Mellon University and a B.S. in Computer Science from Columbia University.
Java expert Josh Bloch discusses creating and destroying objects: when and how
to create them, when and how to avoid creating them, how to ensure they are
destroyed in a timely manner, and how to manage any cleanup actions that must
precede their destruction.
Chapter 2
Creating and Destroying Objects
This chapter concerns creating and destroying objects: when and how to create them, when and
how to avoid creating them, how to ensure they are destroyed in a timely manner, and how to manage any cleanup actions that must precede their destruction.
Item 1:
Consider static factory methods instead of constructors
The normal way for a class to allow a client to obtain an instance of itself is to provide a public
constructor. There is another technique that should be a part of every programmer’s toolkit. A class
can provide a public static factory method, which is simply a static method that returns an instance
of the class. Here’s a simple example from Boolean (the boxed primitive class for the primitive
type boolean). This method translates a boolean primitive value into a Boolean object reference:
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
Note that a static factory method is not the same as the Factory Method pattern from Design
Patterns [Gamma95, p. 107]. The static factory method described in this item has no direct equivalent in Design Patterns.
A class can provide its clients with static factory methods instead of, or in addition to, constructors. Providing a static factory method instead of a public constructor has both advantages and
disadvantages.
informit.com/java
4
One advantage of static factory methods is that, unlike constructors, they have names. If
the parameters to a constructor do not, in and of themselves, describe the object being returned, a
static factory with a well-chosen name is easier to use and the resulting client code easier to read.
For example, the constructor BigInteger(int, int, Random), which returns a BigInteger that
is probably prime, would have been better expressed as a static factory method named BigInteger.probablePrime. (This method was eventually added in the 1.4 release.)
A class can have only a single constructor with a given signature. Programmers have been
known to get around this restriction by providing two constructors whose parameter lists differ
only in the order of their parameter types. This is a really bad idea. The user of such an API will
never be able to remember which constructor is which and will end up calling the wrong one by
mistake. People reading code that uses these constructors will not know what the code does without referring to the class documentation.
Because they have names, static factory methods don’t share the restriction discussed in the
previous paragraph. In cases where a class seems to require multiple constructors with the same
signature, replace the constructors with static factory methods and carefully chosen names to highlight their differences.
A second advantage of static factory methods is that, unlike constructors, they are not
required to create a new object each time they’re invoked. This allows immutable classes (Item
15) to use preconstructed instances, or to cache instances as they’re constructed, and dispense them
repeatedly to avoid creating unnecessary duplicate objects. The Boolean.valueOf(boolean)
method illustrates this technique: it never creates an object. This technique is similar to the Flyweight pattern [Gamma95, p. 195]. It can greatly improve performance if equivalent objects are
requested often, especially if they are expensive to create.
The ability of static factory methods to return the same object from repeated invocations allows
classes to maintain strict control over what instances exist at any time. Classes that do this are said
to be instance-controlled. There are several reasons to write instance-controlled classes. Instance
control allows a class to guarantee that it is a singleton (Item 3) or noninstantiable (Item 4). Also,
it allows an immutable class (Item 15) to make the guarantee that no two equal instances exist:
a.equals(b) if and only if a==b. If a class makes this guarantee, then its clients can use the ==
operator instead of the equals(Object) method, which may result in improved performance.
Enum types (Item 30) provide this guarantee.
A third advantage of static factory methods is that, unlike constructors, they can return
an object of any subtype of their return type. This gives you great flexibility in choosing the
class of the returned object.
One application of this flexibility is that an API can return objects without making their classes
public. Hiding implementation classes in this fashion leads to a very compact API. This technique
lends itself to interface-based frameworks (Item 18), where interfaces provide natural return types
for static factory methods. Interfaces can’t have static methods, so by convention, static factory
methods for an interface named Type are put in a noninstantiable class (Item 4) named Types.
For example, the Java Collections Framework has thirty-two convenience implementations of
its collection interfaces, providing unmodifiable collections, synchronized collections, and the like.
Nearly all of these implementations are exported via static factory methods in one noninstantiable
class (java.util.Collections). The classes of the returned objects are all nonpublic.
The Collections Framework API is much smaller than it would have been had it exported
thirty-two separate public classes, one for each convenience implementation. It is not just the bulk
of the API that is reduced, but the conceptual weight. The user knows that the returned object has
informit.com/java
5
precisely the API specified by its interface, so there is no need to read additional class documentation for the implementation classes. Furthermore, using such a static factory method requires the
client to refer to the returned object by its interface rather than its implementation class, which is
generally good practice (Item 52).
Not only can the class of an object returned by a public static factory method be nonpublic, but
the class can vary from invocation to invocation depending on the values of the parameters to the
static factory. Any class that is a subtype of the declared return type is permissible. The class of the
returned object can also vary from release to release for enhanced software maintainability and performance.
The class java.util.EnumSet (Item 32), introduced in release 1.5, has no public constructors,
only static factories. They return one of two implementations, depending on the size of the underlying enum type: if it has sixty-four or fewer elements, as most enum types do, the static factories
return a RegularEnumSet instance, which is backed by a single long; if the enum type has sixtyfive or more elements, the factories return a JumboEnumSet instance, backed by a long array.
The existence of these two implementation classes is invisible to clients. If RegularEnumSet
ceased to offer performance advantages for small enum types, it could be eliminated from a future
release with no ill effects. Similarly, a future release could add a third or fourth implementation of
EnumSet if it proved beneficial for performance. Clients neither know nor care about the class of
the object they get back from the factory; they care only that it is some subclass of EnumSet.
The class of the object returned by a static factory method need not even exist at the time the
class containing the method is written. Such flexible static factory methods form the basis of service provider frameworks, such as the Java Database Connectivity API (JDBC). A service provider
framework is a system in which multiple service providers implement a service, and the system
makes the implementations available to its clients, decoupling them from the implementations.
There are three essential components of a service provider framework: a service interface,
which providers implement; a provider registration API, which the system uses to register implementations, giving clients access to them; and a service access API, which clients use to obtain an
instance of the service. The service access API typically allows but does not require the client to
specify some criteria for choosing a provider. In the absence of such a specification, the API returns
an instance of a default implementation. The service access API is the “flexible static factory” that
forms the basis of the service provider framework.
An optional fourth component of a service provider framework is a service provider interface,
which providers implement to create instances of their service implementation. In the absence of
a service provider interface, implementations are registered by class name and instantiated reflectively (Item 53). In the case of JDBC, Connection plays the part of the service interface, DriverManager.registerDriver is the provider registration API, DriverManager.getConnection is
the service access API, and Driver is the service provider interface.
There are numerous variants of the service provider framework pattern. For example, the service access API can return a richer service interface than the one required of the provider, using the
Adapter pattern [Gamma95, p. 139]. Here is a simple implementation with a service provider interface and a default provider:
// Service provider framework sketch
// Service interface
public interface Service {
... // Service-specific methods go here
}
informit.com/java
6
// Service provider interface
public interface Provider {
Service newService();
}
// Noninstantiable class for service registration and access
public class Services {
private Services() { }
// Prevents instantiation (Item 4)
// Maps service names to services
private static final Map<String, Provider> providers =
new ConcurrentHashMap<String, Provider>();
public static final String DEFAULT_PROVIDER_NAME = “<def>”;
// Provider registration API
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
providers.put(name, p);
}
// Service access API
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider p = providers.get(name);
if (p == null)
throw new IllegalArgumentException(
“No provider registered with name: “ + name);
return p.newService();
}
}
A fourth advantage of static factory methods is that they reduce the verbosity of creating parameterized type instances. Unfortunately, you must specify the type parameters when you invoke the
constructor of a parameterized class even if they’re obvious from context. This typically requires
you to provide the type parameters twice in quick succession:
Map<String, List<String>> m =
new HashMap<String, List<String>>();
This redundant specification quickly becomes painful as the length and complexity of the type
parameters increase. With static factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose that HashMap provided this
static factory:
informit.com/java
7
Material is excerpted from
Effective Java, Second Edition
9780321356680 | Joshua Bloch | ©Pearson Education
informit.com/title/9780321356680
Continue reading about creating and destroying objects
http://www.informit.com/articles/article.aspx?p=1216151
Want to hear more from Josh Bloch on Effective Java? See more at Effective Java Part I:
http://www.informit.com/podcasts/episode.aspx?e=4731a039-7853-46a1-aa98-765739864fc7
Effective Java Part II:
http://www.informit.com/podcasts/episode.aspx?e=1ea8d72e-ee68-4376-a5b0-5cc73828c1cb
Article 2—Clean Code
Robert C. “Uncle Bob” Martin has been a software professional since 1970
and an international software consultant since 1990. He is founder and president of Object Mentor, Inc., a team of experienced consultants who mentor their
clients worldwide in the fields of C++, Java, C#, Ruby, OO, Design Patterns, UML,
Agile Methodologies, and eXtreme programming.
Even bad code can function. But if code isn’t clean, it can bring a development
organization to its knees. Every year, countless hours and significant resources
are lost because of poorly written code. But it doesn’t have to be that way. Noted
software expert Robert C. Martin presents a revolutionary paradigm with “Clean
Code.”
Chapter 2
Clean Code
You are reading this book for two reasons. First, you are a programmer. Second, you want to be a
better programmer. Good. We need better programmers.
This is a book about good programming. It is filled with code. We are going to look at code
from every different direction. We’ll look down at it from the top, up at it from the bottom, and
through it from the inside out. By the time we are done, we’re going to know a lot about code.
What’s more, we’ll be able to tell the difference between good code and bad code. We’ll know how
to write good code. And we’ll know how to transform bad code into good code.
informit.com/java
8
There Will Be Code
One might argue that a book about code is somehow behind the timesčthat code is no longer the
issue; that we should be concerned about models and requirements instead. Indeed some have suggested that we are close to the end of code. That soon all code will be generated instead of written. That programmers simply won’t be needed because business people will generate programs
from specifications.
Nonsense! We will never be rid of code, because code represents the details of the requirements. At some level those details cannot be ignored or abstracted; they have to be specified. And
specifying requirements in such detail that a machine can execute them is programming. Such a
specification is code.
I expect that the level of abstraction of our languages will continue to increase. I also expect
that the number of domain-specific languages will continue to grow. This will be a good thing. But
it will not eliminate code. Indeed, all the specifications written in these higher level and domainspecific language will be code! It will still need to be rigorous, accurate, and so formal and
detailed that a machine can understand and execute it.
The folks who think that code will one day disappear are like mathematicians who hope one
day to discover a mathematics that does not have to be formal. They are hoping that one day we
will discover a way to create machines that can do what we want rather than what we say. These
machines will have to be able to understand us so well that they can translate vaguely specified
needs into perfectly executing programs that precisely meet those needs.
This will never happen. Not even humans, with all their intuition and creativity, have been able
to create successful systems from the vague feelings of their customers. Indeed, if the discipline of
requirements specification has taught us anything, it is that well-specified requirements are as formal as code and can act as executable tests of that code!
Remember that code is really the language in which we ultimately express the requirements.
We may create languages that are closer to the requirements. We may create tools that help us parse
and assemble those requirements into formal structures. But we will never eliminate necessary precision—so there will always be code.
Bad Code
I was recently reading the preface to Kent Beck’s book Implementation Patterns.1 He says,
“. . . this book is based on a rather fragile premise: that good code matters. . . .” A fragile premise? I disagree! I think that premise is one of the most robust, supported, and overloaded of all the
premises in our craft (and I think Kent knows it). We know good code matters because we’ve had
to deal for so long with its lack.
I know of one company that, in the late 80s, wrote a killer app. It was very popular, and lots
of professionals bought and used it. But then the release cycles began to stretch. Bugs were not
repaired from one release to the next. Load times grew and crashes increased. I remember the day
I shut the product down in frustration and never used it again. The company went out of business
a short time after that.
Two decades later I met one of the early employees of that company and asked him what had
happened. The answer confirmed my fears. They had rushed the product to market and had made
a huge mess in the code. As they added more and more features, the code got worse and worse until
they simply could not manage it any longer. It was the bad code that brought the company down.
informit.com/java
9
Have you ever been significantly impeded by bad code? If you are a programmer of any experience then you’ve felt this impediment many times. Indeed, we have a name for it. We call it wading. We wade through bad code. We slog through a morass of tangled brambles and hidden pitfalls.
We struggle to find our way, hoping for some hint, some clue, of what is going on; but all we see
is more and more senseless code.
Of course you have been impeded by bad code. So thenčwhy did you write it? Were you trying to go fast? Were you in a rush? Probably so. Perhaps you felt that you didn’t have time to do a
good job; that your boss would be angry with you if you took the time to clean up your code. Perhaps you were just tired of working on this program and wanted it to be over. Or maybe you looked
at the backlog of other stuff that you had promised to get done and realized that you needed to slam
this module together so you could move on to the next. We’ve all done it.
We’ve all looked at the mess we’ve just made and then have chosen to leave it for another day.
We’ve all felt the relief of seeing our messy program work and deciding that a working mess is better than nothing. We’ve all said we’d go back and clean it up later. Of course, in those days we
didn’t know LeBlanc’s law: Later equals never.
The Total Cost of Owning a Mess
If you have been a programmer for more than two or three years, you have probably been significantly slowed down by someone else’s messy code. If you have been a programmer for longer than
two or three years, you have probably been slowed down by messy code. The degree of the slowdown can be significant. Over the span of a year or two, teams that were moving very fast at the
beginning of a project can find themselves moving at a snail’s pace. Every change they make to the
code breaks two or three other parts of the code. No change is trivial. Every addition or modification to the system requires that the tangles, twists, and knots be “understood” so that more tangles,
twists, and knots can be added. Over time the mess becomes so big and so deep and so tall, they
can not clean it up. There is no way at all.
As the mess builds, the productivity of the team continues to decrease, asymptotically
approaching zero. As productivity decreases, management does the only thing they can; they add
more staff to the project in hopes of increasing productivity. But that new staff is not versed in the
design of the system. They don’t know the difference between a change that matches the design
intent and a change that thwarts the design intent. Furthermore, they, and everyone else on the
team, are under horrific pressure to increase productivity. So they all make more and more messes,
driving the productivity ever further toward zero. (See Figure 1-1.)
Material is excerpted from
Clean Code: A Handbook of Agile Software Craftsmanship
9780132350884 | Robert C. Martin | ©Pearson Education
informit.com/title/9780132350882
Continue reading about how to build cleaner code
http://www.informit.com/content/images/9780132350884/samplepages/0132350882_Sample.pdf
Having trouble working with legacy code? Watch Mike Feather discuss best practices
http://www.informit.com/podcasts/episode.aspx?e=11b5d715-2884-4b87-b9b4-86a8a5e34d6a
informit.com/java
10
Article 3—Implementation Patterns
Kent Beck, one of the software industry’s most creative and acclaimed leaders,
consistently challenges software engineering dogma and promotes ideas like
patterns, test-driven development, and Extreme Programming. Currently affiliated
with Three Rivers Institute and Agitar Software, he is the author of many AddisonWesley titles, including Test-Driven Development (2003) and, with Cynthia
Andres, Extreme Programming Explained, Second Edition (2005.
Kent Beck shows how to express the behavior of a program through a given set of
patterns.
Chapter 7
Behavior
John Von Neumann contributed one of the primary metaphors of computing—a sequence of
instructions that are executed one by one. This metaphor permeates most programming languages,
Java included. The topic of this chapter is how to express the behavior of a program. The patterns
are:
• Control Flow—Express computations as a sequence of steps.
• Main Flow—Clearly express the main flow of control.
• Message—Express control flow by sending a message.
• Choosing Message—Vary the implementors of a message to express choices.
• Double Dispatch—Vary the implementors of messages along two axes to express cascading
choices.
• Decomposing Message—Break complicated calculations into cohesive chunks.
• Reversing Message—Make control flows symmetric by sending a sequence of messages to
the same receiver.
• Inviting Message—Invite future variation by sending a message that can be implemented in
different ways.
• Explaining Message—Send a message to explain the purpose of a clump of logic.
• Exceptional Flow—Express the unusual flows of control as clearly as possible without interfering with the expression of the main flow.
informit.com/java
11
• Guard Clause—Express local exceptional flows by an early return.
• Exception—Express non-local exceptional flows with exceptions.
• Checked Exception—Ensure that exceptions are caught by declaring them explicitly.
• Exception Propagation—Propagate exceptions, transforming them as necessary so the information they contain is appropriate to the catcher.
Control Flow
Why do we have control flow in programs at all? There are languages like Prolog that don’t have
an explicit notion of a flow of control. Bits of logic float around in a soup, waiting for the right
conditions before becoming active.
Java is a member of the family of languages in which the sequence of control is a
fundamental organizing principle. Adjacent statements execute one after the other. Conditionals
cause code to execute only in certain circumstances. Loops execute code repeatedly. Messages are
sent to activate one of several subroutines. Exceptions cause control to jump up the stack.
All of these mechanisms add up to a rich medium for expressing computations. As an
author/programmer, you decide whether to express the flow you have in mind as one main flow
with exceptions, multiple alternative flows each of which is equally important, or some
combination. You group bits of the control flow so they can be understood abstractly at first, for
the casual reader, with greater detail available for those who need to understand them. Some
groupings are routines in a class, some are by delegating control to another object.
Main Flow
Programmers generally have in mind a main flow of control for their programs. Processing starts
here, ends there. There may be decisions and exceptions along the way, but the computation has a
path to follow. Use your programming language to clearly express that flow.
Some programs, particularly those that are designed to work reliably in hostile circumstances,
don’t really have a visible main flow. These programs are in the minority, however. Using the
expressive power of your programming language to clearly express little-executed, seldomchanged facts about your program obscures the more highly leveraged part of your program: the
part that will be read, understood, and changed frequently. It’s not that exceptional conditions are
unimportant, just that focusing on expressing the main flow of the computation clearly is more
valuable.
Therefore, clearly express the main flow of your program. Use exceptions and guard clauses
to express unusual or error conditions.
Message
One of the primary means of expressing logic in Java is the message. Procedural languages use
procedure calls as an information hiding mechanism:
informit.com/java
12
compute() {
input();
process();
output();
}
says, “For purposes of understanding this computation all you need to know is that it consists of
these three steps, the details of which are not important at the moment.” One of the beauties of programming with objects is that the same procedure also expresses something richer. For every
method, there is potentially a whole set of similarly structured computations whose details differ.
And, as an extra added bonus, you don’t have to nail down the details of all those future variations
when you write the invariant part.
Using messages as the fundamental control flow mechanism acknowledges that change is the
base state of programs. Every message is a potential place where the receiver of the message can
be changed without changing the sender. Rather than saying “There is something out there the
details of which aren’t important,” the message-based version of the procedure says, “At this point
in the story something interesting happens around the idea of input. The details may vary.” Using
this flexibility wisely, making clear and direct expressions of logic where possible and deferring
details appropriately, is an important skill if you want to write programs that communicate
effectively.
Choosing Message
Sometimes I send a message to choose an implementation, much as a case statement is used in procedural languages. For example, if I am going to display a graphic in one of several ways, I will
send a polymorphic message to communicate that a choice will take place at runtime.
public void displayShape(Shape subject, Brush brush) {
brush.display(subject);
}
The message display() chooses the implementation based on the runtime type of the brush. Then
I am free to implement a variety of brushes: ScreenBrush, PostscriptBrush, and so on.
Liberal use of choosing messages leads to code with few explicit conditionals. Each choosing
message is an invitation to later extension. Each explicit conditional is another point in your
program that will require explicit modification in order to modify the behavior of the whole
program.
Reading code that uses lots of choosing messages requires skill to learn. One of the costs of
choosing messages is that a reader may have to look at several classes before understanding the
details of a particular path through the computation. As a writer you can help the reader navigate
by giving the methods intention-revealing names. Also, be aware of when a choosing message is
overkill. If there is no possible variation in a computation, don’t introduce a method just to
provide the possibility of variation.
informit.com/java
13
Double Dispatch
Choosing messages are good for expressing a single dimension of variability. In the example in
“Choosing Message,” this dimension was the type of medium on which the shape was to be drawn.
If you need to express two independent dimensions of variability, you can cascade two choosing
messages.
For example, suppose I wanted to express that a Postscript oval was computed differently
than a screen rectangle. First I would decide where I wanted the computations to live. The base
computations seeem like they belong in the Brush, so I will send a choosing message first to the
Shape, then to the Brush:
displayShape(Shape subject, Brush brush) {
subject.displayWith(brush);
}
Now each Shape has the opportunity to implement displayWith() differently. Rather than
do any detailed work, however, they append their type onto the message and defer to the Brush:
Oval.displayWith(Brush brush) {
brush.displayOval(this);
}
Rectangle.displayWith(Brush brush) {
brush.displayRectangle(this);
}
Now the different kinds of brushes have the information they need to do their work:
PostscriptBrush.displayRectangle(Rectangle subject) {
writer print(subject.left() +” “ +...+ “ rect);
}
Double dispatch introduces some duplication with a corresponding loss of flexibility. The
type names of the receivers of the first choosing message get scattered over the methods in the
receiver of the second choosing message. In this example, this means that to add a new Shape, I
would have to add methods to all the Brushes. If one dimension is more likely to change than the
other, make it the receiver of the second choosing message.
The computer scientist in me wants to generalize to triple, quadruple, quintuple dispatch.
However, I’ve only ever attempted triple dispatch once and it didn’t stay for long. I have always
found clearer ways to express multi-dimensional logic.
Decomposing (Sequencing) Message
When you have a complicated algorithm composed of many steps, sometimes you can group
related steps and send a message to invoke them. The intended purpose of the message isn’t to provide a hook for specialization or anything sophisticated like that. It is just old-fashioned functional
decomposition. The message is there simply to invoke the sub-sequence of steps in the routine.
Decomposing messages need to be descriptively named. Most readers should be able to
gather what they need to know about the purpose of the sub-sequence from the name alone. Only
informit.com/java
14
those readers interested in implementation details should have to read the code invoked by the
decomposing message.
Difficulty naming a decomposing message is a tip-off that this isn’t the right pattern to use.
Another tip-off is long parameter lists. If I see these symptoms, I inline the method invoked by the
decomposing message and apply a different pattern, like Method Object, to help me communicate
the structure of the program.
Reversing Message
Symmetry can improve the readability of code. Consider the following code:
void compute() {
input();
helper.process(this);
output();
}
While this method is composed of three others, it lacks symmetry. The readability of the method
is improved by introducing a helper method that reveals the latent symmetry. Now when reading
compute(), I don’t have to keep track of who is sent the messages—they all go to this.
Material is excerpted from
Implementation Patterns
9780321413093 | Kent Beck| ©Pearson Education
informit.com/title/9780321413093
Continue reading about behavior patterns.
http://www.informit.com/articles/article.aspx?p=1149121
Want to write better, more maintainable automated tests? Watch Gerard Meszaros discuss how to
improve your testing with xUnit Test Patterns
http://www.informit.com/podcasts/episode.aspx?e=56de908c-a71c-46c2-ae77-ae1f81f749b1
Article 4—Core Servlets
Marty Hall is the president of coreservlets.com, a leading provider of Java training and consulting services. Larry Brown is an IT manager at a U.S. Navy
Research and Development Laboratory).Yaakov Chaikin, senior consultant at a
software development company based in Columbia, MD, heads the Web Development track at Loyola College’s graduate computer science program.
Learn how to create and use custom tags utilizing the new SimpleTag API, which
was introduced in version 2.4 of the servlet specification. As its name suggests,
SimpleTag API is very easy to use in comparison to its predecessor, now known as
the classic tag API.
informit.com/java
15
As discussed in Volume 1 (Section 11.2) of Core Servlets and JavaServer Pages, you have many
options when it comes to generating dynamic content inside the JSP page. These options are as follows:
• Scripting elements calling servlet code directly
• Scripting elements calling servlet code indirectly (by means of utility classes)
• Beans
• Servlet/JSP combo (MVC)
• MVC with JSP expression language
• Custom tags
The options at the top of the list are much simpler to use and are just as legitimate as the
options at the bottom of the list. However, industry has adopted a best practice to avoid placing
Java code inside the JSP page. This best practice stems from it being much harder to debug and
maintain Java code inside the JSP page. In addition, JSP pages should concentrate only on the presentation logic. Introducing Java code into the JSP page tends to divert its purpose and, inevitably,
business logic starts to creep in. To enforce this best practice, version 2.4 of the servlet specification went so far as to provide a way to disable any type of JSP scripting for a group of JSP pages.
We discuss how to disable scripting in Section 2.14 (Configuring JSP Pages).
That said, there are cases where the presentation logic itself is quite complex and using the
non-Java code options in the JSP page to express that logic becomes either too clunky and unreadable or, sometimes, just impossible to achieve. This is where the custom tags come in. They provide a way to create the complex presentation logic inside a regular Java class and at the same time
allow the JSP code to refer to that logic through the familiar HTML-like structures.
This chapter discusses how to create and use custom tags utilizing the new SimpleTag API,
which was introduced in version 2.4 of the servlet specification. As its name suggests, SimpleTag
API is very easy to use in comparison to its predecessor, now known as the classic tag API.
Although the SimpleTag API completely replaces the classic tag API, you should keep in mind
that it works only in containers compliant with servlet specification 2.4 and above. Because there
are still a lot of applications running on servlet 2.3-compliant containers, you should consider
avoiding the SimpleTag API if you are not sure what type of container your code will end up on.
7.1
Tag Library Components
• To use custom JSP tags, you need to define three separate components:
• The tag handler class that defines the tag’s behavior
• The TLD file that maps the XML element names to the tag implementations
• The JSP file that uses the tag library
The rest of this section gives an overview of each of these components, and the following sections give details on how to build these components for various styles of tags. Most people find that
the first tag they write is the hardest—the difficulty being in knowing where each component
should go, not in writing the components. So, we suggest that you start by just downloading the
informit.com/java
16
simplest of the examples of this chapter from http://volume2.coreservlets.com/ and getting those
examples to work on your machine. After that, you can move on and try creating some of your own
tags.
The Tag Handler Class
When defining a new tag, your first task is to define a Java class that tells the system what to do
when it sees the tag. This class must implement theSimpleTag interface. In practice, you extend
SimpleTagSupport, which implements the SimpleTag interface and supplies standard implementations for some of its methods. Both the SimpleTag interface and the SimpleTagSupport class
reside in the javax.servlet.jsp.tagext package.
The very first action the container takes after loading the tag handler class is instantiating it with
its no-arg constructor. This means that every tag handler must have a no-arg constructor or its instantiation will fail. Remember that the Java compiler provides one for you automatically unless you
define a constructor with arguments. In that case, be sure to define a no-arg constructor yourself.
The code that does the actual work of the tag goes inside the doTag method. Usually, this code
outputs content to the JSP page by invoking the print method of the JspWriter class. To obtain an
instance of the JstWriter class you call getJspContext().getOut() inside the doTag method.
The doTag method is called at request time. It’s important to note that, unlike the classic tag model,
the SimpleTag model never reuses tag handler instances. In fact, a new instance of the tag handler
class is created for every tag occurrence on the page. This alleviates worries about race conditions
and cached values even if you use instance variables in the tag handler class.
You place the compiled tag handler in the same location you would place a regular servlet,
inside the WEB-INF/classes directory, keeping the package structure intact. For example, if your
tag handler class belongs to the mytags package and its class name is MyTag, you would place the
MyTag.class file inside the WEB-INF/classes/mytags/ directory.
Listing 7.1 shows an example of a tag handler class.
Listing 7.1
Example Tag Handler Class
package somepackage;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
public class ExampleTag extends SimpleTagSupport {
public void doTag() throws JspException, IOException {
JspWriter out = getJspContext().getOut();
out.print(“<b>Hello World!</b>”);
}
}
The Tag Library Descriptor File
Once you have defined a tag handler, your next task is to identify this class to the server and to
associate it with a particular XML tag name. This task is accomplished by means of a TLD file in
XML format. This file contains some fixed information (e.g., XML Schema instance declaration),
informit.com/java
17
an arbitrary short name for your library, a short description, and a series of tag descriptions. Listing 7.2 shows an example TLD file.
Listing 7.2
Example Tag Library Descriptor Class
<?xml version=”1.0” encoding=”UTF-8” ?>
<taglib xmlns=”http://java.sun.com/xml/ns/j2ee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd”
version=”2.0”>
<tlib-version>1.0</tlib-version>
<short-name>csajsp-taglib</short-name>
<tag>
<description>Example tag</description>
<name>example</name>
<tag-class>package.TagHandlerClass</tag-class>
<body-content>empty</body-content>
</tag>
</taglib>
We describe the details of the contents of the TLD file in later sections. For now, just note that
the tag element through the following subelements in their required order defines the custom tag.
• description. This optional element allows the tag developer to document the purpose of the
custom tag.
• name. This required element defines the name of the tag as it will be referred to by the JSP
page (really tag suffix, as will be seen shortly).
• tag-class. This required element identifies the fully qualified name of the implementing
tag handler class.
• body-content. This required element tells the container how to treat the content between the
beginning and ending occurrence of the tag, if any. The value that appears here can be either
empty, scriptless, tagdependent, or JSP.
The value of empty means that no content is allowed to appear in the body of the tag. This
would mean that the declared tag can only appear in the form:
<prefix:tag/>
or
<prefix:tag></prefix:tag>
(without any spaces between the opening and closing tags). Placing any content inside the
tag body would generate a page translation error.
The value of scriptless means that the tag body is allowed to have JSP content as long as
it doesn’t contain any scripting elements like <% ... %> or <%= ... %>. If present, the body
of the tag would be processed just like any other JSP content.
The value of tagdependent means that the tag is allowed to have any type of content as its
body. However, this content is not processed at all and completely ignored. It is up to the developer of the tag handler to get access to that content and do something with it. For example, if
informit.com/java
18
you wanted to develop a tag that would allow the JSP page developer to execute an SQL statement, providing the SQL in the body of the tag, you would use tagdependent as the value of
the body-content element.
Finally, the value of JSP is provided for backward compatibility with the classic custom tag
model. It is not a legal value when used with the SimpleTag API.
Note that there is no legal way of allowing any scripting elements to appear as the tag body
under the new SimpleTag API model.
Core Warning
When using the SimpleTag API, it is illegal to include scripting elements in the body of
the tag.
The TLD file must be placed inside the WEB-INF directory or any subdirectory thereof.
Core Note
The TLD file must be placed inside the WEB-INF directory or a subdirectory thereof.
We suggest that you don’t try to retype the TLD every time you start a new tag library, but start
with a template. You can download such a template from http://volume2.coreservlets.com/.
The JSP File
Once you have a tag handler implementation and a TLD, you are ready to write a JSP file that
makes use of the tag. Listing 7.3 gives an example. Somewhere in the JSP page you need to place
the taglib directive. This directive has the following form:
<%@ taglib uri=”...” prefix=”...” %>
The required uri attribute can be either an absolute or relative URL referring to a TLD file like the
one shown in Listing 7.2. For now, we will use a simple URL relative to the Web application’s root
directory. This makes it easy to refer to the same TLD file from multiple JSP pages in different
directories. Remember that the TLD file must be placed somewhere inside the WEB-INF directory.
Because this URL will be resolved on the server and not the client, it is allowed to refer to the
WEB-INF directory, which is always protected from direct client access.
The required prefix attribute specifies a prefix to use in front of any tag name defined in the
TLD of this taglib declaration. For example, if the TLD file defines a tag named tag1 and the prefix attribute has a value of test, the JSP page would need to refer to the tag as test:tag1. This tag
could be used in either of the following two ways, depending on whether it is defined to be a container that makes use of the tag body:
<test:tag1>Arbitrary JSP</test:tag1>
or just
<test:tag1 />
informit.com/java
19
Listing 7.3
Example JSP File
<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.0 Transitional//EN”>
<HTML>
<HEAD>
<TITLE>Example JSP page</TITLE>
<LINK REL=STYLESHEET
HREF=”JSP-Styles.css”
TYPE=”text/css”>
</HEAD>
<BODY>
<%@ taglib uri=”/WEB-INF/tlds/example.tld”
prefix=”test” %>
<test:example/>
<test:example></test:example>
</BODY></HTML>
Material is excerpted from
Core Servlets and JavaServer Pages, Volume II
9780131482609 | Marty Hall, Larry Brown, and Yaakov Chaikin | ©Pearson Education
informit.com/title/9780131482609
Continue reading about creating and using custom tags
http://www.informit.com/articles/article.aspx?p=1161218
What’s new in Java and Open Source? Watch Core Java author Cay Horstmann discusses recent
innovations
http://www.informit.com/podcasts/episode.aspx?e=e4d28ea5-3426-4bb8-bf29-4d42302d625d
informit.com/java
20
Additional Resources
A complete list of available resources for Java programmers, including Pod
Casts, Articles, Books, and Reference Guides can be found at
informit.com/java.
All books are available in Safari Books Online
9780132354769 | Core Java, Volume I: Fundamentals | Cay Horstmann
and Gary Cornell
informit.com/title/9780132354769
9780132354790 | Core Java, Volume II: Advanced Features |
Cay Horstmann and Gary Cornell
informit.com/title/9780132354790
9780672329432 | Sams Teach Yourself Java 6 in 21 Days |
Rogers Cadenhead and Laura Lemay
informit.com/title/9780672329432
9780131738867 | Core Java Server Faces | David Geary and
Cay Horstmann
informit.com/title/9780131738867
9780321503107 | Next Generation Java Testing: TestNG and Advanced
Concepts | Cedric Beust and Hani Suleiman
Informit.com/title/9780321503107
9780321349606 | Java Concurrency in Practice | Brian Goetz
informit.com/title/9780321349606
9780131872486 | Thinking in Java | Bruce Eckel
informit.com/title/9780131872486
informit.com/java
21
LiveLessons: Self-Paced, Personal Video Training from the World’s
Leading Technology Experts
Jumpstart your career learning on more than a dozen technologies.
0137131135 Java Fundamentals I and II (Video Training)
See complete lists of available LiveLessons at
http://www.informit.com/promotions/promotion.aspx?promo=135366
ROUGH CUTS
Rough Cuts give you exclusive access to an evolving manuscript. Get
access to books as they’re being developed, well before publication. This is a great way
to get a “sneak peak” into new technologies.
0137156790 Seam Framework
See complete list of available Rough Cuts at informit.com/roughcuts
SHORT CUTS
Technology is evolving faster than ever. As a technical professional, you
need to know more than ever, and you need to know it now....which is why we publish
“Short Cuts,” your short cut to technical mastery. Short Cuts are short, concise, PDF
documents designed specifically for busy technical professionals like you.
0131584650 Google Web Toolkit Solutions Digital Short Cut
See complete list of available Short Cuts at informit.com/shortcuts
SAFARI BOOKS ONLINE
Safari Books Online is the e-reference resource for technology and
business professionals. If you do not already have Safari Books Online try it today with a
free trial and gain unlimited access to thousands of books and video from leading
technology publishers. To sign up go to: informit.com/safaritrial
OnSoftware
Conversations & tips from the industry’s leading developers across a wide
range of programming and development topics. informit.com/podcasts
informit.com/java
22