Table of Contents

Book: practical API design

Do not expose more than you want

Use methods instead of instances

Factory is better than Constructors

Problem:

public final class Template extends Object {
private final Class type;
public Template(Class type) { this.type = type; }
public Class getType() { return type; }
public Template() { this(Object.class); }
}

When we migrated the NetBeans sources to JDK 1.5, it was natural to parameterize the Template class with a type parameter identifying the internal class object. This worked fine . . .

public final class Template<T> extends Object {
private final Class<T> type;
public Template(Class<T> type) { this.type = type; }
public Class<T> getType() { return type; }
// now what!?
 
public Template() { this(Object.class); }
}

Solution:

public final class Template<T> extends Object {
private final Class<T> type;
public Template(Class<T> type) { this.type = type; }
public Class<T> getType() { return type; }
@Deprecated
@SuppressWarnings("unchecked")
public Template() { this((Class<T>)Object.class); }
public static Template<Object> create() {
return new Template<Object>(Object.class);
}
}

Use final everywhere

Prohibit sub-classing with final key word if it's not desired.

Allow Access Only from Friend Code

Friend accessor example:

It might sometimes be useful to extend a set of friends to a wider range of classes. For example, you might want to define two packages, one for the pure API and the other for the implementation. If you take this approach, the following trick might be useful. Imagine there is an Item class, as follows:

public final class Item {
private int value;
private ChangeListener listener;
static {
Accessor.setDefault(new AccessorImpl());
}
/** Only friends can create instances. */
Item() {
}
/** Anyone can change value of the item.
*/
public void setValue(int newValue) {
value = newValue;
ChangeListener l = listener;
if (l != null) {
l.stateChanged(new ChangeEvent(this));
}
}
/** Anyone can get the value of the item.
*/
public int getValue() {
return value;
}
/** Only friends can listen to changes.
*/
void addChangeListener(ChangeListener l) {
assert listener == null;
listener = l;
}
}

This class is part of the API, but cannot be instantiated or listened to outside the friend classes that are in the API package and other packages. In this scenario, you can define an Accessor in the non-API package:

public abstract class Accessor {
private static volatile Accessor DEFAULT;
public static Accessor getDefault() {CHAPTER 5DO NOT EXPOSE MORE THAN YOU WANT
Accessor a = DEFAULT;
if (a != null) {
return a;
}
try {
Class.forName(
Item.class.getName(), true, Item.class.getClassLoader()
);
} catch (Exception ex) {
ex.printStackTrace();
}
return DEFAULT;
}
public static void setDefault(Accessor accessor) {
if (DEFAULT != null) {
throw new IllegalStateException();
}
DEFAULT = accessor;
}
public Accessor() {
}
protected abstract Item newItem();
protected abstract void addChangeListener(Item item, ChangeListener l);
}

This Accessor has abstract methods to access all the “friend” functionality of the Item class, with a static field to get the accessor’s instance. The main trick is to implement the Accessor with a nonpublic class in the API package:

final class AccessorImpl extends Accessor {
protected Item newItem() {
return new Item();
}
protected void addChangeListener(Item item, ChangeListener l) {
item.addChangeListener(l);
}
}

You register the Accessor as the default instance the first time someone touches api.Item. Do this by adding a static initializer to the Item class:

static {
Accessor.setDefault(new AccessorImpl());
}

Now the friend code can use the accessor to invoke the hidden functionality from the impl package:

Item item = Accessor.getDefault().newItem();
assertNotNull("Some item is really created", item);
Accessor.getDefault().addChangeListener(item, this);

Give the creator of an object more control

public static Executor create(Configuration config) {
return new Fair(config);
}
public static final class Configuration {
boolean fair;
int maxWaiters = -1;
public void setFair(boolean fair) {
this.fair = fair;
}
public void setMaxWaiters(int max) {
this.maxWaiters = max;
}
}

With this approach you can stop adding new factory methods, and if evolution requires it, add new setters to the Configuration class instead. Adding such methods is safe, as the class is final and the only use for it is as an argument to the create factory method. Its usefulness is restricted outside this scenario, as it intentionally has no public getters. That’s why adding new methods to it can only affect the internals of the factory method, which are under the API writer’s control. You can therefore extend the internals to handle the additional variations in the input configuration.

Moreover, introducing the Configuration class solves the last outstanding issue—that is, the ability of the privileged code to perform its privileged operation after the nonprivileged code has already accessed the object. This was impossible with the setReadOnly pattern, as well as with the plain factory method. Now, the privileged code can continue to keep a refer- ence to the Configuration instance and call its methods to modify parameters of Executor instances already given to nonprivileged users. Such users’ code has no chance to get a refer- ence to the Configuration instance; casting doesn’t help because these are two different objects. The Configuration acts like a secret token, which cannot be obtained through an API. So, this solution is completely safe and secure.

Code against interface not implementation

Interfaces for immutable

Adding methods to interface requires that all client implementations of that interface to be changed. The interface must be subclassed in order to provide additional method:

public interface InstanceProvider {
  public Class<?> instanceClass() throws Exception;
  public Object instanceCreate() throws Exception;
}
 
public interface BetterInstanceProvider extends InstanceProvider {
  public boolean isInstanceOf(Class<?> c);
}

Now all the clients around our code, as well as in all modules written by third parties, are supposed to check if the provider they have reference to is “better” by doing instanceof, and if so, cast and invoke its isInstanceOf method.

This creates new problems. First of all, all client code using just InstanceProvider will be rewritten. This is not possible, as the code is spread all over the world and nobody has complete control over it, so it’s still likely clients will be using the old approach and thus performance won’t improve. Also, the client code is getting more and more complicated. Every client has to do the instanceof check and handle both alternatives. If it’s true, it needs to use the new method; if it fails, it needs to revert back to the old behavior of myClass. isAssignableFrom(provider.instanceClass()). Also, spreading such “if” statements throughout the entire code base isn’t nice at all.

if (instance instanceof BetterInstanceProvider) {
BetterInstanceProvider bip = (BetterInstanceProvider)instance;
return bip.isInstanceOf(String.class);
} else {
return String.class.isAssignableFrom(instance.instanceClass());
}

That’s why I prefer Java interfaces to clearly specify an interface that isn’t about to change.

A Method Addition Lover’s Heaven

While Java interfaces are completely immutable with respect to method addition, there’s the opposite extreme. In this scenario, it’s not only possible to add methods, but it’s also possible to make them completely binary compatible. In Java this can be expressed in the form of the final class.

Therefore, when you need to add a new method to a class or interface, select the final class. With the final class, you won’t encounter the problem of complicated client code that was described in the interfaces section. Say the InstanceProvider is a final class:

import java.util.concurrent.Callable;
public final class InstanceProvider {
private final Callable<Object> instance;
public InstanceProvider(Callable<Object> instance) {
this.instance = instance;
}
public Class<?> instanceClass() throws Exception {
return instance.call().getClass();
}
public Object instanceCreate() throws Exception {
return instance.call();
}
}

Then, it’s easy and safe to add new methods to it and provide a default implementation, so that the class becomes the following:

import
import
import
import
java.util.Arrays;
java.util.HashSet;
java.util.Set;
java.util.concurrent.Callable;
public final class InstanceProvider {
private final Callable<Object> instance;
private final Set<String> types;
public InstanceProvider(Callable<Object> instance) {
this.instance = instance;
this.types = null;
}
/** Specifies not only a factory for creating objects, but
* also additional information about them.
* @param instance the factory to create the object
* @param type the class that the create object will be instance of
* @since 2.0
*/
public InstanceProvider(Callable<Object> instance, String... types) {
this.instance = instance;
this.types = new HashSet<String>();
this.types.addAll(Arrays.asList(types));
}
public Class<?> instanceClass() throws Exception {
return instance.call().getClass();
}
public Object instanceCreate() throws Exception {
return instance.call();
}
/** Allows to find out if the InstanceProvider creates object of given
* type. This check can be done without loading the actual object or
* its implementation class into memory.
*
* @param c class to test
* @return if the instances produced by this provider is instance of c
* @since 2.0
*/
public boolean isInstanceOf(Class<?> c) throws Exception {
if (types != null) {
return types.contains(c.getName());
} else {
// fallback
return c.isAssignableFrom(instanceClass());
}
}
}

The difference with the interfaces example is that the clients of the API in version 2.0 don’t need to care about whether the actual instance handles their call to isInstanceOf in a better way or if it’s a fallback to the old implementation.

Are Abstract Classes Useful?

Up until this point, it has been argued that you should use Java interfaces when you want to define an immutable contract and that you should use Java final classes when you want to have the ability to add methods. So it's reasonable to ask, “Is there any reason to use abstract classes?”.

The short answer is “No”. But abstract class allows defining static methos, which is not possible with interface.

Getting Ready for Growing Parameters

For example, say you have a method that was called by the framework to compute the content of a UI list. Later you realize that you not only need that content, but you also need some additional text to describe or classify the computed data. If that happens, you can add a new method that calls the previous one, as follows:

public abstract class Compute {
/**
* @return list of strings to work with
* @since 1.0 */
public abstract List<String> getData();
/
** Computes the strings to work with together with their
* associated descriptions. Shall be overriden in subclasses.
* By default delegates to {@link #getData}
* and uses the provided strings as both, the string
* and its description.
*
* @return name to description pairs to work with
* @since 2.0 */
public Map<String,String> getDataAndDescription() {
LinkedHashMap<String,String> ret =
new LinkedHashMap<String, String>();
for (String s : getData()) {
ret.put(s, s);
}
return ret;
}
}

This is possible, but only if the method is inside a class. On top of that, giving clients a class to implement isn’t as clean a solution as giving them an interface. Also, adding methods into a class that is often subclassed is slightly dangerous. That is why it’s preferred to use a Java interface for the Compute class. However, then you can give up on the need to add new meth- ods. Although it’s possible to create an extended interface, a much better situation, with respect to evolution, can be created by means of the response/reply pattern:

public interface Compute {
public void computeData(Request request, Response response);
public final class Request {
// only getters public, rest hidden only for friend code
Request() {
}
}
public final class Response {
// only setters public, rest available only for friend code
private final Map<String,String> result;
/** Allow access only to friend code */
Response(Map<String,String> result) {
this.result = result;
}
public void add(String s) {
result.put(s, s);
}
public void addAll(List<String> all) {
for (String s : all) {
add(s);
}
}
/** @since 2.0 */
public void add(String s, String description) {
result.put(s, description);
}
}
}

In this setup, you can easily achieve any extension to the method parameters by adding new getters into the Compute.Request class, which is immutable and final, and therefore its methods can only be called. That means that you can add more methods there without any risk.

interfaces vs. Classes

In summary, always code against interfaces and not implementations. Remember, this doesn’t mean Java interfaces, but abstract definitions. When coding in Java, use Java interfaces to specify immutable types and use final classes as the type where methods can be safely added. When designing more intricate structures, think about extensibility and choose the appropri- ate style, just like in the request and response example. Sometimes it’s acceptable to use abstract classes. However, remember that if the goal is 100 percent binary compatibility, never add methods to classes or interfaces that someone else can implement.

Javaworld - Java design articles

Sources

Design techniques articles

object initialization

Two important design guidelines this article attempts to promote are the “canonical object design” and “omni-valid state principle.” The canonical object design An object should have state, represented by instance variables that are private. Invoking an object's instance methods should be the only way code defined in other classes can affect the object's state.

The omni-valid state principle Objects should have a valid state, and experience only valid state transitions, from the beginning of their lifetimes to the end.

One last guideline pertains to the nature of guidelines themselves. The guidelines proposed in this column are not proposed as laws you should blindly follow at all times but as rules of thumb you'll probably want to follow much of the time. They are intended to help you acquire a mindset conducive to good design. Thus, the final guideline is:

Designing fields & methods

This article covered some very fundamental territory, which can be summarized as three guidelines: The especially valuable guideline for fields

The constant corollary

Maximizing method cohesion & avoiding method explosion

The gist of this article can be summarized in these guidelines:

Objects finalization and clean up

The most important point to take away from this article is that if a Java object needs to take some action at the end of its life, no automatic way exists in Java that will guarantee that action is taken in a timely manner. You can't rely on finalizers to take the action, at least not in a timely way. You will need to provide a method that performs the action and encourage client programmers to invoke the method when the object is no longer needed.

This article contained several guidelines that pertain to finalizers:

Remember that exceptions thrown by finalizers are ignored If your program includes objects with finalizers that absolutely must be run before the program exits, invoke runFinalizersOnExit(true) in class Runtime or System Unless you are writing the finalizer for class Object, always invoke super.finalize() at the end of your finalizers

Designing with exceptions

The most important point to take away from this article is that exceptions are there for abnormal conditions and shouldn't be used to report conditions that can be reasonably expected as part of the everyday functioning of a method. Although the use of exceptions can help make your code easier to read by separating the “normal” code from the error handling code, their inappropriate use can make your code harder to read.

Here is a collection of the exception guidelines put forth by this article:

Design for thread safety

The most important point to take away from this article is that when programming in Java, you should at least think about thread safety every time you design a class.

Here's a collection of the exception guidelines put forth by this article:

The event generator idiom

http://www.artima.com/designtechniques/eventgenP.html

Implementation guidelines

With the guidelines I list in this section, I am trying to define a default way to implement this idiom. I say default because, unless you have a specific reason to take a different implementation approach, you should automatically use the approach recommended in these guidelines. My theory is that if you adhere closely to the default implementation approach, it will be easier for your fellow programmers to recognize the idiom in your work. More importantly, I feel that such idiom recognition will make it easier for your fellow programmers to understand, use, and change your code.

On the other hand, you should feel free to depart from the default approach to implementing the idiom when you feel it makes sense. In fact, I myself describe two potential “variants” to the default approach in the next section.

Now, on to the guidelines:

Composition versus inheritance

Comparing composition and inheritance

So how exactly do composition and inheritance compare? Here are several points of comparison:

Choosing between composition and inheritance

So how do all these comparisons between composition and inheritance help you in your designs? Here are a few guidelines that reflect how I tend to select between composition and inheritance.

Designing with interface

Interface guidelines Where, then, do interfaces fit into this picture? As I mention above, one major benefit of the Java interface is that they give composition a shot at polymorphism. When you use composition with interfaces, it becomes as easy to add a new front-end class (composition) as it is to add a new subclass (inheritance). But what does this tell us? Should you always use interfaces every time you use composition? Well, no. Should you avoid using interfaces in conjunction with single inheritance of class extension? Certainly not.

As I mentioned at the beginning of this article, it took me a long time to get the point of interfaces. The epiphany finally came when I recognized that separation interface and implementation is one of the primary ideas behind Java in general. The Java virtual machine (JVM), for example, is an abstract computer that defines the way your program “interfaces” with the underlying real computer. A JVM that runs on Windows is one implementation of that abstract computer. A JVM that runs on the Macintosh is another. A JVM that runs on your wristwatch is yet another.

Likewise, the Java APIs are designed not to give you access to specific capabilities of particular computers and operating systems, but define abstract interfaces through which your programs talks to the underlying concrete computer and operating system, whatever it is. Swing, for example, provides an interface through which your Java program can create graphical user interfaces on whatever platform happens to be underneath. You can even use Swing to create user-interfaces on your wristwatch, so long as someone has done the work to implement Swing on your wristwatch.

Separation of interface and implementation is central to Java's spirit, and the Java interface construct enables you to achieve this separation in your designs. Two major activities of any software system design are identifying parts (the subsystems within a program or system of programs) and specifying the interfaces between the parts. In designing a Java-based system, you should use Java interfaces to represent abstract interfaces – the ways in which the parts will interact with each other.

So this is how I ended up thinking about Java's interfaces: as the preferred means of communicating with the parts of your program that represent abstractions that may have several implementations. For example, two parts of a program I describe in my September Design Techniques installment, “The Event Generator Idiom”, were TelephoneListener and Telephone. In this design, I decided that the “telephone listener” represented an abstraction that could have multiple implementations, but that Telephone did not. Thus, I made Telephone a class that didn't implement any interfaces, and defined TelephoneListener as an interface. Telephone, an event source, passed events to (communicated with) listeners through the TelephoneListener interface.

I see interfaces as a fundamental tool for achieving flexibility in the design of Java-based systems. Any class can provide an implementation of an interface. As long as you don't change the interface itself, you can make all kind of changes to the implementing classes, or plug in new classes, without impacting code that depends only on the interface. Thus, if you have a subsystem that represents an abstraction that may have multiple implementations, whether the subsystem is a single object, a group of objects, an entire Java applet or application, you should define Java interfaces through which the rest of the world communicates with that subsystem. When you use interfaces in this way, you decouple the parts of your system from each other and generate code that is more flexible: more easily changed, extended, and customized.

Class vs object