Using Object-Oriented Patterns - Beginning Java Programming: The Object-Oriented Approach (Programmer to Programmer) (2015)

Beginning Java Programming: The Object-Oriented Approach (Programmer to Programmer) (2015)

12. Using Object-Oriented Patterns

WHAT YOU WILL LEARN IN THIS CHAPTER:

· What programming patterns are and why they are useful

· How can object-oriented patterns be applied to structure applications

· Which libraries, frameworks, and best practices are commonly used in Java programming

WROX.COM CODE DOWNLOADS FOR THIS CHAPTER

The wrox.com code downloads for this chapter are found at www.wrox.com/go/beginningjavaprogramming on the Download Code tab. The code is in the Chapter 12 download and individually named according to the names throughout the chapter.

Congratulations, you’ve reached the last chapter. The primary goal behind this chapter is to combine all the knowledge you’ve gained throughout this book and to show you how all the various pieces—control flow, input and output, class inheritance, and so on—come together to construct a complete application.

Since many aspiring programmers find it hard to start from a blank sheet and maintain order and structure in their applications, we aim to reach the goals listed above with a chapter on so-called programming patterns. This chapter will teach you a number of best practices and solutions for common problems you’ll encounter when creating object-oriented applications with Java. As such, this chapter starts with a general introduction on programming patterns, before delving deeper into the patterns. Finally, the chapter ends with a brief overview of some third-party libraries that you may find useful when starting new projects.

INTRODUCTION TO PATTERNS

The concept of the design pattern is well known in the area of software engineering. Many definitions for “pattern” exist, but broadly speaking, a pattern is a general applicable and reusable solution that solves frequently occurring problems and architectural challenges in software design. In other words, a pattern provides a description on how to solve a particular problem, which is applicable in many situations.

Interestingly, the notion of a pattern did not originate in the realm of programming or software engineering, but as an architectural concept. In 1977, Christopher Alexander put forth the idea of capturing architectural design principles as reusable descriptions, formulated in his work, A Pattern Language: Towns, Buildings, Construction. He stated that “each pattern describes a problem that occurs over and over again in our environment and then describes the core of the solution to that problem in such a way that you can use this solution a million times over without ever doing it the same way twice.” Then, in 1987, Kent Beck and Ward Cunningham started experimenting with the idea of applying patterns to programming and presented their results at object-oriented software engineering conferences. From that moment onward, software and programming “design patterns” have been popular in computer science.

The big surge in interest, however, followed the publication of the book Design Patterns: Elements of Reusable Object-Oriented Software in 1994 by Gamma, Helm, Johnson, and Vlissides, commonly referred to as the “Gang of Four.” The book describes 23 design patterns for use in object-oriented software projects and has become highly influential in the field of software engineering. So much so that it is incredibly hard to find a mid- to large-scale Java, C++, Smalltalk, or other Object-Oriented Programming language-based project that doesn’t contain one of these design patterns.

Remember that a pattern defines a concrete solution, meaning that you should be able to use a pattern repeatedly once the reference is given, and the solution must be applicable in lots of different situations. To describe and write down these patterns, many different forms have been proposed, ranging from free-form, narrative texts to structured forms containing well-defined sections (the Gang of Four books, for instance, applies a very structured form). In addition, a repository or catalogue should also be structured in such a way that specific patterns can be easily and quickly retrieved. To facilitate this, patterns are often structured in so-called pattern languages or pattern groups: a set of patterns aiming to solve the same general problem.

The “Gang of Four” book structures patterns as being Creational, Structural, or Behavioral. Creational patterns deal with object creation mechanisms, as the basic form of object creation could result in design problems or added complexity to the design. Structural patterns ease the design by identifying a simple way to realize relationships between entities. Behavioral patterns identify common communication patterns between objects to increase flexibility in carrying out this communication.

OBJECT-ORIENTED PATTERNS

This section takes you through a handful of the original Gang of Four design patterns. It’s presented in a relaxed style, where instead of providing the pattern in a very strict and structured form (you can read the original book for that), you see how the pattern works, how you can spot it in real-life projects, how you can use it yourself, and then common pitfalls you should watch out for. Don’t expect that all patterns will immediately be useful to you as a beginner, but you’ll note that you will feel a natural tendency toward them as you become more experienced.

This book adopts the same structure and naming as the Gang of Four, dividing the patterns into Creational, Structural, and Behavioral categories. Keep in mind that the focus here is on how each pattern works and behaves in Java. The goal here is to show you the patterns that also teach you how to make architectural design choices and how each choice allows certain freedoms and takes away others. This section doesn’t copy the description of the original patterns verbatim, as this would not offer much value.

Creational Patterns

Creational patterns deal with object creation. Instead of creating objects directly, these patterns offer more flexibility in terms of deciding which objects need to be created in a given case, meaning that they offer alternatives for the following standard creation procedure in Java, as you’ve seen throughout this book:

House myHouse = new House();

Here, the singleton, static utility class, service provider (this pattern is not mentioned in the Gang of Four book but is closely related to the singleton pattern), factory, and abstract factory patterns are covered. The builder and prototype creational patterns are not discussed, as both of them are less commonly applied in Java.

Singleton Pattern and Static Utility Class

Let’s start with one of the most controversial object-oriented patterns, the singleton pattern. (We’ll address the reasons behind its controversy later on.) The Gang of Four summarizes a design pattern as follows:

Ensure a class has instance and provide a global point of access to that instance.

This pattern is useful whenever exactly one object is needed to coordinate certain actions. In Java, an easy way to define a singleton class is as follows:

public class Singleton {

private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

public static Singleton getInstance() {

return INSTANCE;

}

// Other public methods follow here

}

You call the singleton’s methods as follows:

Singleton.getInstance().theSingletonMethod();

Note the different aspects at play here. First of all, the static final field is immediately initialized to contain the single singleton object (which cannot be changed afterward due to being final). Second, direct instantiation of objects is prohibited by making the constructor private. Third, the single instance is accessed calling the public static getInstance() method.

This way of defining a singleton class is called “eager initialization,” as the single instance gets created no matter whether it will be used during the execution of the program. The alternative, called “lazy initialization,” first sets the INSTANCE (nonfinal, then) field to null and initializes it the first time the getInstance() method is called. Using this approach, however, can introduce subtle bugs in multithreaded Java programs (where multiple tasks are executed in parallel) if not implemented correctly, so it is not advisable.

Another interesting, less-used way to define a singleton in Java is by using an enum type:

public enum Singleton {

INSTANCE;

// Other public methods follow here

}

This method is preferred by some, as it takes advantage of Java’s guarantee that enum values are instantiated only once in a Java program, are globally accessible, and are instantiated lazily without potential issues regarding multithreading. You use the singleton enumas follows:

Singleton.INSTANCE.theSingletonMethod();

Now that you know how the singleton pattern works, it is time for a word of advice: this pattern is one of the most overused and badly used design patterns out there. The reason for this is easy to see: the singleton pattern provides an easy way out whenever you want to introduce global variables or state in your program. Why is this a bad thing? By allowing global state in your program, you allow any other parts of your program to access and modify this at any time, making these programs very hard to test and debug when something goes wrong (who modified the singleton in a bad manner?).

In many real-life cases, less experienced programmers will resort to using singletons whenever they feel that they need a “manager” class to store some centralized information, perform some centralized task, or babysit other objects. For example, take a look at the following extreme case:

public class Account {

private String name;

private double amount;

public String getName() {

return name;

}

public double getAmount() {

return amount;

}

public void setName(String name) {

this.name = name;

}

public void setAmount(double amount) {

this.amount = amount;

}

}

public class AccountManager {

public Account createAccount(String name, double startFunds) {

Account account = new Account();

account.setName(name);

account.setAmount(startFunds);

return account;

}

public boolean isOverdrawn(Account account) {

return account.getAmount() < 0;

}

public void addFunds(Account account, double amount) {

account.setAmount(account.getAmount() + amount);

}

}

This example can look a bit silly—rightly so—although note that there exist many cases of code with such anti-object-oriented design once you look behind the curtain. In cases such as these, it’s easy to imagine that AccountManager should be a singleton. Any account will need a manager, and how many managers do you need? Of course, the correct answer is not one, but none. You can resolve this problem like so:

public class Account {

private String name;

private double amount;

public Account(String name) {

this(name, 0);

}

public Account(String name, double startAmount) {

this.name = name;

this.amount = startAmount;

}

public boolean isOverdrawn() {

return this.amount < 0;

}

public void addFunds(double amount) {

this.amount += amount;

}

public String getName() {

return name;

}

public double getAmount() {

return amount;

}

}

You can use the Account class as is to encapsulate the data and the behavior, which has to do with the concept of an account. This is exactly what Object-Oriented Programming is about. Poorly designed singleton classes are oftentimes “helpers” or “utility” classes that add functionality to other classes. If possible, just move that behavior to the class itself.

NOTE This misuse of the singleton is captured in the so-called “god object” anti-pattern. Just as patterns recommend best practices, anti-patterns indicate commonly made mistakes and depict bad design decisions. A god object is a centralized object that contains and does too many things. It shows up in all other places of the codebase and becomes too tightly coupled to the other code.

There are cases, however, where the use of singletons is helpful. A big insight lies in the fact that the singleton pattern solves two problems at once:

· Limits a class to a single instance.

· Provides a global point of access to an instance.

In Java, you should look at both requirements separately. If you want to limit a class to a single instance, this doesn’t mean you want to allow every other object out there to access this instance. In those cases, providing a public, global point of access is a weak architectural decision. How, then, do you allow only one creation of a class? Like so:

public class Singleton {

private static boolean CREATED = false;

public Singleton() {

if (CREATED)

throw new RuntimeException(

"Class "+this.getClass()+" can only be created once");

CREATED = true;

}

// Other methods follow here

public static void main(String args[]) {

Singleton one = new Singleton();

Singleton two = new Singleton();

}

}

The singleton class can now be constructed at any time in any place, but will throw an error if you try to construct it more than once. This code ensures single instantiation, but does not dictate how the class is used (i.e., not a global point of access). This is a simple mechanism that’s somewhat underused.

Providing a global point of access to an instance is the second and often the main reason that singletons are used. This concept makes it easy to reach for an object that’s utilized in many places, but sometimes in places where you don’t want it to be used. So what options are there to keep things organized?

In some cases, you can choose to pass in the object you need as an argument to the methods that need it. However, it can be argued that many objects don’t fit well in the argument list of a method. For instance, when every method that’s added to a log file also has aLog object in its argument list, things get messy quickly.

NOTE In many cases, singletons pop up for things that have to be used and accessed throughout a codebase. Logging is a traditional example. This concept is called “cross-cutting concerns,” and an alternative programming paradigm—aspect-oriented programming—was designed to address these concerns. Libraries exist to bring aspect-oriented features to Java, but their use is not very widespread. Refactoring your code and trying to keep things as neat as possible is the preferred way to go.

For “global” objects that only have to be accessed by a class and its subclasses, it is a good idea to make the singleton object a field of the superclass, together with a private method such as getObjectOfInterest(). The subclasses derived from the superclass then have access to the object they need, without the object being accessible to other classes and their instances.

For global classes that offer a set of utility functions, consider making the class and all its methods static to build a “static utility class.” In many cases, this is a better approach and one you’ll see in many Java programs. Many third-party libraries offer classes ending in “Utils” (FileUtils, StringUtils, and so on), and they contain nothing else but public static helper methods. Although these methods are also globally accessible, the class itself does not contain any state. Naturally, this point only holds when you do not define static variables in the helper class that’s being modified by the methods. The only reason why static utility classes should contain static fields is to define helpful constants (such as the static double PI variable in the Math class). If you do decide to create a static utility class, however, always keep in mind the earlier example, and try to encapsulate as much behavior as possible in the appropriate classes themselves whenever possible (no AccountUtils!).

STATIC UTILITY CLASS OR SINGLETON?


You might be wondering what the difference is between the following two ways to define a singleton class in Java:

// As a static utility class:

public class Singleton {

private static int A = 0;

public static int get() {

return A;

}

public static void set(int a) {

A = a;

}

}

// A single instance of a class:

public class Singleton {

private static final Singleton INSTANCE = new Singleton();

private int A;

private Singleton() {

A = 0;

}

public static Singleton getInstance() {

return INSTANCE;

}

public int get() {

return A;

}

public void set(int a) {

A = a;

}

}

Ignoring the fact that using static variables to keep state is not good practice, both of these classes seem to achieve the same end, although there is a subtle difference. Consider a Logger singleton class with a log(String message) method. Imagine that, at first, this method just shows a message on the console, but later on, you want to add functionality to log a file and retain the ability to choose at runtime which of the two implementations to use. With a static utility class, you would have to resort to a check in the log method to see which path of execution to follow. If the singleton contains more methods, the check has to be added to every method, reducing maintainability and introducing redundancy. With the non-static singleton object, you can make the log method abstract and create two subclasses—ConsoleLogger and FileLogger. You can then determine the right object to assign as the instance, without modifying any other code. The second way is thus better practice from an Object-Oriented Programming viewpoint. That said, remember that whenever you are defining a utility class without its own state and thus holding a collection of simple utility functions, it is better to define it as a static class.

To conclude this discussion on the singleton pattern, consider the following guidelines:

· Always check whether you actually need a separate class or whether the behavior belongs in an existing class.

· When you need a globally accessible list of utility methods, use a static utility class without modeling any state (static variables that are being changed by the class methods).

· When you do need a single instance of a class, think about whether the instance also has to be globally accessible.

· If you do need a single instance of a class and it has to be globally accessible, you can use the full singleton pattern. Always be wary of this option, and make sure you’ve exhausted the other options mentioned here first.

As discussed, most singletons offer some general “helper” methods or provide a global centralized service for use in the rest of the application, such as a logging functionality. To avoid having a global collection of singleton objects, it is a good idea to group your singletons under a service provider, as described in the next pattern.

Service Provider Pattern and Null Object Pattern

Let’s say you have a service offering logging functionality that needs to be called throughout all parts of your code. Perhaps you should implement it as a static utility class, as you’ve seen previously:

LoggingUtils.logMessage("Something bad happened", LoggingUtils.PRIORITY_HIGH);

Or perhaps create a singleton:

Logger.getInstance().logMessage("Something bad happened", Logger.PRIORITY_HIGH);

Disregarding the fact that this approach introduces some nasty global objects in the project, you still have the problem that these objects are very tightly coupled with the rest of the code. What if at one point you want to introduce a second logging system and decide which one to use at runtime?

The service provider pattern can help, because it allows you to keep a “registry” of services and swap them in and out at runtime. This is illustrated in the logging example. Imagine you have a Logger interface and two concrete implementing classes, ConsoleLogger andEmailLogger (an AbstractLogger can be defined here as well, for instance to hold the different priority level constants). Your service locator would then look like this:

public class ServiceProvider {

private static final ServiceLocator INSTANCE = new ServiceLocator();

private ServiceLocator() {}

private Logger logger;

public static ServiceLocator getInstance() {

return INSTANCE;

}

private Logger getLogger() {

return logger;

}

private void setLogger(Logger logger) {

this.logger = logger;

}

}

The service locator has some interesting benefits. First, it is not possible to change the service implementation at runtime. Second, the service provider is a singleton, but the services don’t have to be. You can still prevent multiple instantiation, but instead of making five singletons globally accessible, everything can now go through one single locator. Third, adding services is just a simple manner of adding a field and the getter and setter methods.

In essence, the service provider pattern is a combination of a singleton and strategy pattern (as you’ll see later).

One thing that you do need to consider, however, is remembering to set all services. When you forget to call the setLogger method in this example, for instance, the ServiceProvider will return null. There are several ways around this. You can manually check in your code to see if a provider returns null, you can force initialization by accepting a Logger object in the ServiceProvider constructor, or you can just initialize the service to a default one. Finally, you can also apply another pattern, the null object pattern, to solve this issue.

The basic idea of the null object is that instead of returning null, a special object returning the same interface as the desired object is returned, but with an implementation that does nothing. In the logging example, a NullLogger would look like this:

public class NullLogger implements Logger {

public void logMessage(String message, int PRIORITY_LEVEL) {

// Do nothing

}

}

As you can see, this service implements all expected methods, but does not actually do anything. Now, when you default to this service in your provider class, the calling code doesn’t have to handle nulls.

NOTE The discussion on the null object pattern is also related to another commonly observed best practice, namely the fact that it is always better—whenever possible—to have functions return empty collections (collections containing zero elements) instead of a null value). A common reason why programmers choose to return null in the first place is to signal to the calling code that some kind of error occurred (the collection could not be retrieved or constructed), which is different from a successful execution returning an empty collection. In this case (signaling errors), the better practice is to rely on exceptions, which can be caught and handled accordingly by calling code. Following this practice will help greatly to avoid null pointer exceptions (e.g., by calling the size() method on a null object).

(Abstract) Factory Pattern

The factory pattern is another commonly applied creational pattern. It’s summarized as follows: Defer instantiation of a class to subclasses.

In other words, the factory pattern provides an interface for creating an object, while providing different implementations for the creation of that object.

As an example, suppose you have an abstract class called Account, subclassed to NormalAccount and VIPAccount. Maybe the VIPAccount gets a bonus on creation, or a bonus every time funds are added, but the exact details of the implementation are not important.

Instead of directly using these objects as is, the factory pattern abstracts away the creation of them by defining separate creation methods that can be overridden. For the account example, the factory pattern can thus be implemented as follows:

public class AccountFactory {

public Account createAccount(String name, double amount) {

return new NormalAccount(name, amount);

}

}

public class VIPAccountFactory extends AccountFactory {

public Account createAccount(String name, double amount) {

return new VIPAccount(name, amount);

}

}

In other parts of the code, either of the following can be defined:

AccountFactory factory = new AccountFactory();

Or:

AccountFactory factory = new VIPAccountFactory();

Either one can call the same createAccount method to create the appropriate account.

Note that you can also choose to implement the factory methods directly in the account classes as static methods, making the constructor of the class private. This is another pattern that shows up a lot in Java projects.

Again, in other cases, different factory classes implement a single interface class. Sometimes, all factory classes subclass an abstract factory superclass (this is specifically referred to as the “abstract factory pattern”) to encapsulate multiple related creation methods. Consider, for example, the abstract factory class FurnitureFactory, which provides abstract definitions for createChair() and createTable(), among others. This abstract factory can then be subclassed to the concrete WoodFurnitureFactory and PlasticFurnitureFactoryclasses, which would each create corresponding objects (WoodChair, PlasticTable, and so on), all of which are also subclasses of abstract Chair and Table classes. The benefit from using this pattern lies in the fact that all remaining code using these factories can work with the abstract Chair and Table classes, instead of the specific version the factory created. This approach retains more levels of abstraction.

Another reason that factory classes are often applied is to create a centralized location to keep a list of all created objects. For instance, you might opt to keep a list of all created accounts in the AccountFactory class and provide methods to easily retrieve a specific account later on. This is fine in some cases, but just as with the singleton pattern, you should take care not to construct overloaded factory classes containing too much behavior and logic. On a similar note, you might encounter factory objects that are also singletons. When this happens, keep an eye out for possible ways to restructure and streamline the affected code.

Structural Patterns

Structural patterns concern how classes and objects are structured, mainly how inheritance and interfaces should be applied to design, or redesign, object-oriented architectures in useful ways.

Most of these patterns involve wrapping of some kind, i.e., creating classes that contain instances of other classes or subclass other classes. Such patterns often show up in cases when code is being refactored or when complex classes are wrapped in simpler ones.

The adapter, bridge, decorator, façade, and composite patterns are discussed here. The type pattern, which is not explicitly mentioned in the Gang of Four book, but deals with an interesting and useful aspect of Object-Oriented Programming, is also included here as an explicit pattern. The structural proxy and flyweight patterns are not addressed, as their use cases are less relevant for beginners (feel free to look them up, however).

Adapter Pattern

The goal of the adapter pattern is to make classes with incompatible interfaces work together, as illustrated here. Imagine a world with two printer companies. One is called Ink4Ever and has a class somewhere in its driver code that looks like this:

public class InkjetPrinter {

public void print(Document d, int nrCopies) {

// Print code here

}

public void printDuplicate(Document d, int nrCopies) {

// Print code here

}

}

The second printer company, called MajorLaser, has a driver class like this:

public class LaserPrinter {

public void print(Document d, boolean printDuplicate) {

// Print code here

}

}

After many years of fierce competition, the two companies merge to form a new entity, called Ink&Laser. The programmers start working on exciting new printer software, with the final task of creating a unified driver that can work with laser and inkjet printers.

The team decides it would be best to use the following single method throughout the codebase:

public void print(Document d, int nrCopies, boolean printDuplicate)

The question now is how to implement this approach so that it works with the inkjet and laser printer drivers, as they do not agree on their method signatures. One of the programmers suggests just merging the two legacy printers into a new, unified driver, like so:

public class UnifiedPrinter {

public void print(Document d, int nrCopies, boolean printDuplicate) {

if (isLaser()) {

for (int copy = 1; copy <= nrCopies; copy++)

printLaser(d, printDuplicate);

} else {

if (printDuplicate)

printInkjetDuplicate(d, nrCopies);

else

printInkjet(d, nrCopies);

}

}

public void printInkjet(Document d, int nrCopies) {

// Print code here

}

public void printInkjetDuplicate(Document d, int nrCopies) {

// Print code here

}

public void printLaser(Document d, boolean printDuplicate) {

// Print code here

}

public boolean isLaser() {

// Figure out whether this is a laser printer or not

return true;

}

}

At first, this seems like a smart idea, but the project manager disapproves. “This won’t do,” he tells his team, “It is far too hard to merge the two classes into a single one, what with the similarly named variables and all. Even more important, what are we going to do once we start supporting a third type of printer? This is not the object-oriented way to do things.” The programmers are rubbing their heads. What would be the right way to implement this correctly? Starting purely from an object-oriented perspective, a natural place to start is by defining a Printer interface containing the method signature that all printer drivers should support:

public interface Printer {

public void print(Document d, int nrCopies, boolean printDuplicate);

}

Next, InkjetPrinter and LaserPrinter can be renamed LegacyInkjetPrinter and LegacyLaserPrinter, and two new classes can be defined:

public class LaserPrinter implements Printer {

public LegacyLaserPrinter printer = new LegacyLaserPrinter();

@Override

public void print(Document d, int nrCopies, boolean printDuplicate) {

for (int copy = 1; copy <= nrCopies; copy++)

printer.print(d, printDuplicate);

}

}

public class InkjetPrinter implements Printer {

public LegacyInkjetPrinter printer = new LegacyInkjetPrinter();

@Override

public void print(Document d, int nrCopies, boolean printDuplicate) {

if (printDuplicate)

printer.print(d, nrCopies);

else

printer.print(d, nrCopies);

}

}

Note how LegacyInkjetPrinter and LegacyLaserPrinter are now contained as instances in the adapter classes InkjetPrinter and LaserPrinter. This is exactly what this pattern is about: wrapping instances of incompatible classes in adapters. Note that the Printer interface can now be used everywhere in the code to pass around printer objects.

NOTE Of course, you still need to decide whether to use an InkjetPrinter or LaserPrinter class at construction. Since this is a creation issue, you might want to use the factory pattern to construct the appropriate object, based on the hardware configuration detected on the computer.

Finally, note that there is another variant of this pattern where a new class is created that extends all original legacy classes. As you’ve seen before, multiple inheritance is not possible in Java, although one way to apply this pattern would be if all legacy classes also have associated interfaces. The new class can then implement all these interfaces.

Bridge Pattern

Next up is the bridge pattern. This pattern is sometimes confused with the adapter pattern, the reason being that the bridge pattern uses the adapter pattern, but goes a step further. It not only keeps flexibility in the implementation (e.g., to choose which printer class to use, all implementing the interface Printer), but also allows greater abstraction. Say you have the Printer interface and InkjetPrinter and LaserPrinter as implementing classes, perhaps still using the legacy classes. The key point to remember is that you have an interface and different implementations of this interface.

Now, an additional abstraction is added on top of the implementations, a class that will sit between the client code and the implementations. That is a bridging class. In the example, this class might be called AbstractPrintSpooler:

public abstract class AbstractPrintSpooler {

private Printer printer;

public AbstractPrintSpooler(Printer printer) {

this.printer = printer;

}

public void print(Document d, int nrCopies, boolean printDuplicate) {

printer.print(d, nrCopies, printDuplicate);

}

}

Note how this class reimplements all the methods from the original Printer interface and passes on every command to the contained Printer instance (again, this is an example of the adapter pattern). Instead of using the Printer interface in your client code, you now use AbstractPrintSpooler, however. Note that you can also decide to have AbstractPrintSpooler implement the Printer interface (this forces the abstraction to also implement all methods from the implementation) and to make the class nonabstract if you want to use it as is (which is also perfectly valid). All things considered, it does seem that the AbstractPrintSpooler class is adding nothing of value, so then why would you add another layer of abstraction? The reason is that you can now add concrete subclasses of this abstraction. Imagine for example a GreenPrintSpooler like so:

public class GreenPrintSpooler extends AbstractPrintSpooler {

public GreenPrintSpooler(Printer printer) {

super(printer);

}

public void print(Document d, int nrCopies, boolean printDuplicate) {

printDuplicate = true; // Force to print duplicates

d.setToBlackAndWhite(); // Convert document to black and white

print(d, nrCopies, printDuplicate);

}

}

A concrete implementation of your spooler has now been created that will convert all documents to black and white and force the duplicate option to true before passing on the print command to the implementation. The benefits of this pattern should become clear to you now: bridges are helpful whenever an extra layer of abstraction is needed on top of something else. For example, whether to print green is a separate decision on top of the printer model being used.

Decorator Pattern

The decorator pattern also bears similarities to the adapter pattern. It allows you to add behavior to a single instance of a class without adding this behavior to all instances of the same class (as you would end up with through normal subclassing).

This pattern is commonly used in GUI programming, so it makes sense to include an example from this context. Imagine you have created a Window class to display fancy GUI windows. Now let’s say you want to create a particular variant of these windows that cannot be minimized and needs to stay on-screen. You can simply create a NaggingWindow class that extends the Window class and adds behavior to disable the minimize button. Imagine that, sometime later, you also encounter a need to add scrollbars to some of your windows. No problem, as you can just create a ScrollableWindow class to do this. However, what about windows that should not be minimized and also need scrollbars? One solution might be to add a NaggingScrollableWindow class, but as windows get more and more features, this would quickly lead to a soup of redundant subclasses. One solution is to merge everything in the single Window class, but this would lead to a single, horribly confusing and lengthy class with many methods, such as setHasScrollbars(boolean b). This solution also does not seem like the proper object-oriented way to program things.

Luckily, the decorator pattern can help. This pattern resembles the bridge pattern in that it contains both a combination of interfaces and abstract classes. For this example, you would need the following:

· interface Window: Contains all the method signatures windows need to support, such as setTitle(String title).

· class NormalWindow implements Window: The class implementing the bare-bones window.

· abstract class WindowDecorator implements Window: The abstract decorator class. Just as with the bridge pattern, this class contains an instance of a Window object, as well as methods for all methods the Window interface defines. In the abstract calls, all method calls are passed to the Window object (this is called delegation).

· class NaggingWindowDecorator extends WindowDecorator and other decorators: These classes define the behavior you want to add on top of another object. These classes will override all methods that need to be extended, in most cases also involving a call to the original superclass method, which delegates to the Window object (super.setTitle(title)).

Once you put all of this in place, the complete code for the example above would look as follows. Note that the code sample below makes some “quick and dirty” shortcuts to get the point across, mainly by reusing the JFrame component in our window classes instead of writing a UI widget library from scratch.

import javax.swing.JFrame;

public interface Window {

public void setTitle(String title);

public void addPanel(String message);

public JFrame getJFrame();

}

import javax.swing.BoxLayout;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

public class NormalWindow implements Window {

private final JFrame jFrame;

private final JPanel mainPane;

public NormalWindow() {

this.jFrame = new JFrame();

this.jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

// We allow ourselves to cheat here by first setting up a scrollpane

// as the content pane for every window, with scroll bars hidden

// Decorators are then free to re-enable these

this.mainPane = new JPanel();

this.mainPane.setLayout(new BoxLayout(this.mainPane, BoxLayout.PAGE_AXIS));

JScrollPane scrollPane = new JScrollPane(mainPane,

JScrollPane.VERTICAL_SCROLLBAR_NEVER,

JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

this.jFrame.setContentPane(scrollPane);

this.jFrame.pack();

this.jFrame.setSize(300, 400);

this.jFrame.setVisible(true);

}

public void setTitle(String title) {

jFrame.setTitle(title);

}

public void addPanel(String message) {

JPanel pane = new JPanel();

pane.add(new JLabel(message));

mainPane.add(pane);

}

public JFrame getJFrame() {

return jFrame;

}

}

import javax.swing.JFrame;

public abstract class WindowDecorator implements Window {

private final Window window;

public WindowDecorator(Window window) {

this.window = window;

}

public void setTitle(String title) {

window.setTitle(title);

}

public void addPanel(String message) {

window.addPanel(message);

}

public JFrame getJFrame() {

return window.getJFrame();

}

public Window getWindow() {

return window;

}

}

import java.awt.event.WindowAdapter;

import java.awt.event.WindowEvent;

import javax.swing.JFrame;

public class NaggingWindowDecorator extends WindowDecorator {

public NaggingWindowDecorator(Window window) {

super(window);

// Add a simple message just to show the decorator is working:

getWindow().addPanel("Decorated with NaggingWindowDecorator");

getWindow().getJFrame().setAlwaysOnTop(true);

getWindow().getJFrame().setResizable(false);

// The code below will prevent users from minimizing the window

// by immediately re-opening minimized windows

// In a real life context, you'd of course resort to the full suite

// of Swing components as we've seen before, i.e. using JDialog

getWindow().getJFrame().addWindowListener(new WindowAdapter() {

@Override

public void windowClosing(WindowEvent we) {

}

// The following will prevent minimizing

@Override

public void windowIconified(WindowEvent we) {

getWindow().getJFrame().setState(JFrame.NORMAL);

}

});

}

}

import javax.swing.JScrollPane;

public class ScrollingWindowDecorator extends WindowDecorator {

public ScrollingWindowDecorator(Window window) {

super(window);

// Add a simple message just to show the decorator is working:

getWindow().addPanel("Decorated with ScrollingWindowDecorator");

((JScrollPane)

getWindow().getJFrame().getContentPane()).setHorizontalScrollBarPolicy(

JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

((JScrollPane)

getWindow().getJFrame().getContentPane()).setVerticalScrollBarPolicy(

JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

}

}

public class WindowTester {

public static void main(String[] args) {

Window decoratedWindow1 = new NaggingWindowDecorator(new NormalWindow());

Window decoratedWindow2 = new ScrollingWindowDecorator(new

NaggingWindowDecorator(new NormalWindow()));

}

}

Note that this pattern shares a lot of similarities with the bridge pattern. The difference is that in this pattern, you continue to use the interface (Window) to define objects and the abstract class has to implement this interface as well. In the bridge pattern, the abstract class can (but does not have to) implement the defined interface and will be used in the client code (AbstractPrintSpool is used instead of the Printer interface).

Façade Pattern

The façade pattern also deals with wrapping objects and shares similarities with the adapter pattern. Again, it is related to the previous patterns:

· The adapter pattern converts the interface to the one the client code is expecting.

· The bridge pattern adds a layer of abstraction between the client code and implementations of an interface.

· The decorator pattern allows the addition and combination of behavioral extensions to an original interface.

· The façade pattern aims to provide a simplified interface around a collection of other interfaces.

The façade pattern is like the adapter pattern on steroids: the façade class will contain an instance of every class it wraps in order to provide a unified and simpler interface for the client code that needs it. For example, let’s say you have a class to search for flights between two locations and a class to search for hotels between two locations. Instead of using these two classes as is in your code, it might be a good idea to define a third class (TripPlanner) that wraps around these two classes in order to find a list of suitable flights and hotels for a given set of locations and time period. The client can then use this façade class, which adds an extra layer of decoupling.

NOTE A word of warning for all these “wrapping” patterns. Note that all these patterns connect the wrapped class to their wrappers, meaning that if changes are made to the wrapped subsystems, all adapters, bridges, decorators, facades, and proxies might need to be changed and updated as well. Therefore, don’t go too far when connecting classes together by wrapping them up around each other.

Composite Pattern

The last structural pattern discussed in this chapter is less about wrapping around objects and more about grouping objects together. Luckily, the description of this pattern is easy to understand:

Allows a group of objects to be treated like a single object.

Basically, following the composite pattern boils down to implementing your own collection class, which is subject to the same methods as the members of that collection. This pattern is pretty interesting since it is easy to understand in essence, but there are multiple ways to implement it, all with their advantages and disadvantages.

This idea is illustrated with a simple example. Imagine you’re programming an application for the human resources department. The department oversees a number of employees—project managers and programmers. You need to keep track of their names and salaries. As such, you set out to create the following class:

public class Employee {

private String name;

private double salary;

private boolean manager;

public Employee(String name, double salary, boolean manager) {

this.name = name;

this.salary = salary;

this.manager = manager;

}

public String getName() {

return name;

}

public double getSalary() {

return salary;

}

public boolean isManager() {

return manager;

}

}

You discuss your progress with the HR team. They congratulate you on your work, but the team head mentions the following: “Sorry, I totally forgot to mention this—I thought this would be clear—but managers need to be able to oversee other employees, managers, or programmers, whereas programmers cannot.”

No problem, you think, I’ll just add a list to the Employee class to keep track of employees overseen by other employees:

public class Employee {

private List<Employee> employees;

private String name;

private double salary;

private boolean manager;

public Employee(String name, double salary, boolean manager) {

this.employees = new ArrayList<>();

this.name = name;

this.salary = salary;

this.manager = manager;

}

public void addEmployee(Employee employee) {

if (isManager())

employees.add(employee);

}

public void removeEmployee(Employee employee) {

if (isManager())

employees.remove(employee);

}

public Employee getEmployee(int i) {

return employees.get(i);

}

public int getNrEmployees() {

return employees.size();

}

public String getName() {

return name;

}

public double getSalary() {

return salary;

}

public boolean isManager() {

return manager;

}

}

You discuss your changes with the team head. “Great,” you’re told, “this is exactly what we need!” Throughout the following months, the HR department requests more and more features. “We forgot to mention, we also want to keep track of the languages a programmer knows. Oh, and for managers, we want to keep track of the number of years they have been working at the company. Oh, and we also have a third type of employee that we want to add. . .” You keep expanding and changing your Employee class, and all is well in the world, right? Not so much. By now, your object-oriented senses should start tingling. The right approach to tackle this problem is to add an abstraction by creating separate Employee, Manager, and Programmer classes, like so:

import java.util.ArrayList;

import java.util.List;

public abstract class Employee {

private List<Employee> employees;

private String name;

private double salary;

public Employee(String name, double salary) {

this.employees = new ArrayList<>();

this.name = name;

this.salary = salary;

}

public void addEmployee(Employee employee) {

employees.add(employee);

}

public void removeEmployee(Employee employee) {

employees.remove(employee);

}

public Employee getEmployee(int i) {

return employees.get(i);

}

public int getNrEmployees() {

return employees.size();

}

public String getName() {

return name;

}

public double getSalary() {

return salary;

}

}

public class Manager extends Employee {

public Manager(String name, double salary) {

super(name, salary);

}

}

public class Programmer extends Employee {

public Programmer(String name, double salary) {

super(name, salary);

}

@Override

public void addEmployee(Employee employee) {

// Consider throwing an exception here

}

@Override

public void removeEmployee(Employee employee) {

}

@Override

public Employee getEmployee(int i) {

return null;

}

@Override

public int getNrEmployees() {

return 0;

}

}

Much better! Now every concept has its own class. Note how this immediately gives you a way to implement the composite pattern. You now have an easy way to treat a group of employees the same way as a single employee. The composite pattern makes it very easy to model a “has a” relation, which is particularly helpful when you want to model a tree structure, such as in this example.

To see why this is so helpful, look at the next simple example program, which shows the organizational structure starting from a particular employee:

public class HRExample {

public static void main(String args[]) {

Employee programmerAimee = new Programmer("Aimee", 16000);

Employee programmerBart = new Programmer("Bart", 15000);

Employee programmerSeppe = new Programmer("Seppe", 14000);

Employee managerJane = new Manager("Jane", 30000);

Employee managerWiley = new Manager("Wiley", 35000);

managerWiley.addEmployee(managerJane);

managerWiley.addEmployee(programmerBart);

managerJane.addEmployee(programmerAimee);

managerJane.addEmployee(programmerSeppe);

showOrganigram(managerWiley);

}

public static void showOrganigram(Employee e) {

showOrganigram(e, 1);

}

private static void showOrganigram(Employee e, int level) {

// Recursive function, show employee

System.out.format("%"+(level*2)+"s - %s, " +

"earning %s", "", e.getName(), e.getSalary());

if (e.getNrEmployees() > 0)

System.out.format(" manages %s employees:", e.getNrEmployees());

System.out.println();

for (int i = 0; i < e.getNrEmployees(); i++) {

showOrganigram(e.getEmployee(i), level+1);

}

}

}

Running this code prints the following:

- Wiley, earning 35000.0 manages 2 employees:

- Jane, earning 30000.0 manages 2 employees:

- Aimee, earning 16000.0

- Seppe, earning 14000.0

- Bart, earning 15000.0

Here you can see the benefits of the composite pattern in action. In the recursive function, you don’t need to pay attention to the differences among a programmer, manager, and groups of employees, as you can apply the same methods on all of them, using the single, abstract Employee class.

Note that there is also a second way to model a composite pattern: by having both the composite objects (the objects containing a list) and the member objects (objects without a list) implement a component interface that defines the operations that should be possible on both of them. It would use the following structure:

public interface Component {

public void method1();

public void method2();

// ...

}

public class Member implements Component {

@Override

public void method1() {/*...*/}

@Override

public void method2() {/*...*/}

// Other methods

}

public class Group implements Component {

@Override

public void method1() {/*...*/}

@Override

public void method2() {/*...*/}

public void addMember(Member member) {/*...*/}

public void removeMember(Member member) {/*...*/}

public Member getMember(int i) {/*...*/}

public int nrMembers() {/*...*/}

// Other methods

}

This implementation can be useful when the member and group nodes do not share any behavior other than a particular set of methods defined in the interface, or when you explicitly want to prevent executing list methods (addMember, removeMember ...) on the member objects. Note that it is also possible to combine the two approaches by defining an interface, an abstract class that implements the interface, and subclasses.

THE INTERFACE-ABSTRACT COMBO


This is a good point in the chapter to mention a particularly curious pattern that you might see in large, heavily “architected” codebases: the combination of an abstract class and an interface.

These classes will often be scattered across different packages, so you need to go on a hunt to find the code you’re looking for in the dependency tree. For instance, an interface class might be named IPerson, PersonInterface, or just Person, and be located in the com.bigcompany.models.hr package, or perhaps in com.bigcompany.models.hr.interface. The abstract class implementing this interface might be named AbstractPerson, PersonImpl, PersonImplementation, or Person, and be located incom.bigcompany.models.hr or com.bigcompany.models.hr.impl, perhaps. The subclasses extending the abstract class are then named NormalPerson and VIPPerson, or NormalPersonImpl and VIPPersonImpl, and are located in com.bigcompany.models.hr orcom.bigcompany.models.hr.impl. . .

What is going on here? In terms of, naming, different programmers prefer different strategies, but in general, an interface defines a contract that the implementations should adhere to. This immediately provides a good heuristic for deciding between an interface and an abstract class: a person is a noun. Normal persons and VIP persons are always, by definition, people. Starting with an abstract class makes more sense in this case. An abstract class describes all the subclasses.

The interface describes what the implementing classes should be able to do. Consider the Comparable built-in interface in Java, which describes the implementations that should add a method that allows you to compare one object of the implementing class to another object of that class. Maybe this applies to all persons as well (comparing alphabetically based on name, for instance), or maybe this only applies to some subclasses of a person, for example, VIPPerson. With subclassing, you describe a hierarchy of inherited behavior. With interfaces, you describe a set of behavior that can be implemented by classes.

So why would it make sense to apply both? First of all, using this approach allows a certain freedom. For instance, when you use and pass around the interface in your client code, you can later define a second abstract class (with descendants) that also implements this interface. These two sets of hierarchies can then differ from each other, but since they both implement the same interface, your client code will continue to work as normal, for both abstract classes and all their descendants, without you having to perform heavy refactoring. However, this doesn’t mean that you should go out of your way to always define an interface and an abstract class. If you find yourself adding all the methods in the abstract class as signatures to the interface, the added benefit of the interface itself is little, especially since there is no immediate desire to create a second abstract class implementing that interface. Even if such a desire did arise, it is likely that you could refactor your current class tree to add the new abstract class as a subclass of the existing abstract class, since they appear to share so much behavior anyway.

The best thing to do is pick either an abstract class (a class is a...) or an interface (a class can do...) and refactor only when a need arises.

One particular real-life scenario you have seen before uses the decorator pattern, where the abstract class and its descendants contain most of the shared code and implement an interface (in simple cases, the abstract hierarchy can be replaced with a single class). The decorator classes can then form a second hierarchy of abstract classes and descendants (or can also be implemented as a collection of unrelated classes in simple cases), and also implement the interface. The interface then provides signatures for the functionality the decorators can tweak. Figures 12.1 through 12.4 provide a schematic overview of these cases.

images

Figure 12.1

images

Figure 12.2

images

Figure 12.3

images

Figure 12.4

Type Pattern and Role Pattern

The last structural pattern covered in this chapter is not explicitly mentioned as such by the Gang of Four, but captures a powerful concept that’s worth explaining on its own.

The idea is that you will create a level of abstraction above the classes and instances, meaning the concept of a class is made into a class itself. Each instance of that class, that is, each object, then represents a different class.

This idea might seem a bit confusing at first. Imagine you’re programming an application to manage various kinds of products. Like a good object-oriented programmer, you start out by defining an abstract Product class and then define concrete subclasses: Book,Movie, Music, and so on. As time progresses, maybe your product tree becomes more and more complex, and you end up having more and more subclasses.

Your program is starting to become hard to maintain, with this gigantic product class tree that keeps growing. This is the typical object-oriented issue, right? Indeed, subclassing defines an “is a” relationship, but it’s only one way of establishing this conceptual relationship.

You decide to sit back and think about your problem some more. At a basic level, you’re trying to solve a simple concept. You have a set of products you want to manage and that share some particular attributes and behavior. Each product is of a certain type, a certain class, in your code. What if you tried to create a class for a class? A class conceptualizing the product type? You set out to try this idea:

import java.util.Set;

public class ProductType {

private String name;

private Set<String> properties;

public ProductType(String name, Set<String> properties) {

this.name = name;

this.properties = properties;

}

public String getName() {

return name;

}

public Set<String> getProperties() {

return properties;

}

}

import java.util.HashMap;

import java.util.Map;

public class Product {

private double price;

private String name;

private ProductType type;

private Map<String, Object> typeProperties;

public Product(String name, double price, ProductType type) {

this.name = name;

this.price = price;

this.type = type;

this.typeProperties = new HashMap<>();

for (String property : type.getProperties())

this.typeProperties.put(property, null);

}

public double getPrice() {

return price;

}

public String getName() {

return name;

}

public void setProperty(String property, Object value) {

validateProperty(property);

typeProperties.put(property, value);

}

public Object getProperty(String property) {

validateProperty(property);

return typeProperties.get(property);

}

private void validateProperty(String property) {

if (!typeProperties.containsKey(property))

throw new IllegalArgumentException("Property "+property

+" not valid for type: "+type.getName());

}

}

Next, you try it:

import java.util.HashSet;

public class TypeTest {

public static void main(String[] args) {

ProductType musicType = new ProductType("Music",

new HashSet<String>());

ProductType movieType = new ProductType("Movie",

new HashSet<String>());

// Set some properties for books:

ProductType bookType = new ProductType("Book",

new HashSet<String>(){{

add("title");

add("author");

add("pages");

}});

Product someMusic = new Product("A music track", 0.99, musicType);

Product aBook = new Product("My book", 44.99, bookType);

aBook.setProperty("title", aBook.getName());

aBook.setProperty("author", "Aimee, Bart and Seppe");

aBook.setProperty("pages", 540);

System.out.println("Book title: "+aBook.getProperty("title"));

System.out.println("Book author: "+aBook.getProperty("author"));

System.out.println("Book pages: "+aBook.getProperty("pages"));

}

}

Congratulations, you’ve just implemented the first principle of Object-Oriented Programming (classes and objects) in an object-oriented manner, albeit a bit awkwardly. Your properties are currently all objects and will need to be typecast to be used in a meaningful way. It’s also hard to know which properties a certain type has (this could be fixed by adding a method returning all properties). Also, this system does not support inheritance. Again, this could also be done by getting clever and determining that ProductType objects can have a “parent” ProductType. If a property is set to null, a product type can then ask a parent (if set) to see if they have a value for a particular property, which is the basics of data inheritance. You might also want to modify your ProductType class so it works more like a real constructor, which you could do by adding the following method:

public Product createProduct(String name, double price) {

return new Product(name, price, this);

}

Which can be used like so:

Product someMusic = musicType.createProduct("A music track", 0.99);

Product aBook = bookType.createProduct("My book", 44.99);

Exciting, no? Using this pattern also has the added benefit that product types can be created and destroyed while the program is running, without requiring that you fire up Eclipse, define some new subclasses, compile everything, and ship it out. Just ask the users which properties the new type should have and you’re golden. You could even allow users to save a list of product type definitions to a file, which can be loaded back in.

In fact, using this pattern, you can also go a step further than Java allows. Think about the following ideas, for instance:

· Allow the type of an object to change. With normal subclassing, this would not be possible (and rightly so!). If this is allowed, this pattern is sometimes called a role pattern. Imagine your program is managing employees who can be managers or programmers. You might want to create Employee, Manager, and Programmer classes, the latter two subclassing Employee. But what if an employee’s role changes? Then modeling these concepts as a subclass is not a good idea, and you’re better off with classes for Employee,EmployeeRole (abstract), ManagerRole (concrete subclass), and ProgrammerRole (concrete subclass), and then adding methods to Employee to set and get its role. What if roles can be defined at runtime as well? Then you need to resort to the type pattern explained here.

· Allow an object to have multiple types or roles, by keeping a list of types.

· Allow multiple inheritance, by allowing a type to have a list of parent types. What this means in practice is for you to determine.

In conclusion, the type pattern is a very powerful one, and it allows you to create your own object-oriented paradigm inside an existing one (object-oriented inception). Some warning, however—you will have noticed that this pattern is especially useful when you’re dealing with shared data, that is, a list of properties for product types.

Note that this pattern’s implementation will change a bit when you know up front what the set of properties will be. In that case, just implement this as follows:

· The fields you want to change at runtime can be implemented as fields in the class (Product, for instance). Fields that are not necessary for a certain product can be set to null.

· The fields that are determined by the type are implemented as fields in the type class and set at construction or by setters (e.g., in ProductType). Getters are added to the original class (Product) to fetch information from the type class (ProductType), or the type object is directly exposed (getProductType() in Product).

Note that all of this still has to do with data. When you also want to dynamically construct behavior, things get much more difficult. You’re then better off resorting to subclasses, or you can apply the strategy pattern (which you’ll see later) to dynamically assign another object to your type class from which a method will be called to execute some behavior. As a simple teaser, consider, for example, that you want to implement a method that calculates a discounted price. Sadly, the way this discount is calculated can differ for each product type. In this case, you need to get a bit clever when assigning properties and their values. Why not go a step further as well? The product manager above was basically assuming that all fields in a product type are non-static, non-final, public, and uninitialized. You can change your type class and allow for static final properties that are all public:

import java.util.HashMap;

import java.util.Map;

public class ProductType {

private String name;

private Map<String, Object> staticProperties;

private Map<String, Object> instanceProperties;

public ProductType(String name) {

this.name = name;

this.staticProperties = new HashMap<>();

this.instanceProperties = new HashMap<>();

}

public Product createProduct(String name, double price) {

return new Product(name, price, this);

}

public String getName() {

return name;

}

public void addStaticProperty(String name, Object value) {

staticProperties.put(name, value);

}

public void removeStaticProperty(String name, Object value) {

staticProperties.remove(name);

}

public void addInstanceProperty(String name, Object value) {

instanceProperties.put(name, value);

}

public void removeInstanceProperty(String name, Object value) {

instanceProperties.remove(name);

}

public boolean isInstanceProperty(String name) {

return instanceProperties.containsKey(name);

}

public boolean isStaticProperty(String name) {

return staticProperties.containsKey(name);

}

public Object getStaticProperty(String name) {

return staticProperties.get(name);

}

public Object getInstanceProperty(String name) {

return instanceProperties.get(name);

}

}

NOTE If you’re up for a challenge, try making a general type class supporting static, instance, public, private, and final fields. In addition, try creating a Typeable interface that any class can implement and then add your custom typing functionality on top of it.

Next, rework the Product class:

import java.util.HashMap;

import java.util.Map;

public class Product {

private double price;

private String name;

private ProductType type;

private Map<String, Object> instanceProperties;

public Product(String name, double price, ProductType type) {

this.name = name;

this.price = price;

this.type = type;

this.instanceProperties = new HashMap<>();

}

public double getPrice() {

return price;

}

public String getName() {

return name;

}

public void setProperty(String property, Object value) {

validateProperty(property);

instanceProperties.put(property, value);

}

public Object getProperty(String property) {

validateProperty(property);

if (!instanceProperties.containsKey(property)) {

// Get the default initialized property from the type

return type.getInstanceProperty(property);

}

return instanceProperties.get(property);

}

public Object getStaticProperty(String property) {

if (!type.isStaticProperty(property))

throw new IllegalArgumentException("Static property "+property+

" not valid for type: "+type.getName());

return type.getStaticProperty(property);

}

private void validateProperty(String property) {

if (!type.isInstanceProperty(property))

throw new IllegalArgumentException("Property "+property+

" not valid for type: "+type.getName());

}

}

Your main class will then look like this:

public class TypeTest {

public static void main(String[] args) {

// Product types now have a discount calculator field...

BookDiscountCalculator bookDiscounter = new BookDiscountCalculator();

ProductType bookType = new ProductType("Book");

bookType.addInstanceProperty("title", null);

bookType.addInstanceProperty("author", null);

bookType.addInstanceProperty("pages", 0);

bookType.addStaticProperty("discountCalculator", bookDiscounter);

Product aBook = bookType.createProduct("My book", 44.99);

aBook.setProperty("title", aBook.getName());

aBook.setProperty("author", "Aimee, Bart and Seppe");

aBook.setProperty("pages", 540);

Product anotherBook = bookType.createProduct("My second book", 244.99);

anotherBook.setProperty("title", anotherBook.getName());

anotherBook.setProperty("author", "Patternicus");

anotherBook.setProperty("pages", 800);

showBook(aBook);

showBook(anotherBook);

}

public static void showBook(Product bookProduct) {

System.out.format("Book '%s' from '%s' (%s pages) costs '%s' " +

"––> after discount = %s%n",

bookProduct.getProperty("title"),

bookProduct.getProperty("author"),

bookProduct.getProperty("pages"),

bookProduct.getPrice(),

((BookDiscountCalculator) bookProduct

.getStaticProperty("discountCalculator"))

.getDiscountedPrice(bookProduct.getPrice())

);

}

public static interface DiscountCalculator {

public double getDiscountedPrice(double originalPrice);

}

public static class BookDiscountCalculator implements DiscountCalculator {

@Override

public double getDiscountedPrice(double originalPrice) {

return Math.max(10, originalPrice * 0.60 - 10);

}

}

}

Note how it is now possible to define a discount calculator for each product type.

NOTE If you’ve been following along closely throughout this book, you might remember that Java defines the concept of a class as an actual class, as java.lang.Class. Couldn’t you just use that to make your classes at runtime, programmatically? Sadly, no. The constructor for this class class is not visible. The following will not work:

Class myClass = new Class();

If you want to get clever, you can use Java’s reflection capabilities together with a custom class loader to construct classes on the fly. If you want to see this approach in action, the most basic proof of concept looks like this:

import java.net.URL;

import java.net.URLClassLoader;

import java.nio.file.Files;

import java.nio.file.Path;

import java.nio.file.Paths;

import javax.tools.JavaCompiler;

import javax.tools.ToolProvider;

public class ClassMaker {

public static void main(String[] args) throws Exception {

String source = "public class Test { "

+ "static { System.out.println(

\"A new class enters this life!\"); } "

"public Test() { "

+ " System.out.println(

\"Wow! Who has created me?\");"

+ "}"

+ "}";

Path sourceDir = Files.createTempDirectory("_javaTest");

Path sourceFile = Paths.get(sourceDir + "/Test.java");

Files.write(sourceFile, source.getBytes());

System.out.println("Compiling: "+sourceFile.toString());

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

if (compiler == null) {

System.out.println(

"Could not get compiler. Are you running with JDK?");

System.exit(1);

}

compiler.run(null, null, null, sourceFile.toString());

System.out.println("Making class...");

URLClassLoader classLoader = URLClassLoader.newInstance(

new URL[] {

sourceDir.toUri().toURL()

});

Class<?> cls = Class.forName("Test", true, classLoader);

Object instance = cls.newInstance();

System.out.println("Compiled a new class: "+cls);

System.out.println("Created its instance: "+instance);

}

}

Note that you must run this code with the JDK, which will then produce something like this:

Compiling: C:\Users\n11093\AppData\Local\Temp\

_javaTest560570871985698759\Test.java

Making class...

A new class enters this life!

Wow! Who has created me?

Compiled a new class: class Test

Created its instance: Test@79dd63b4

Truly a Frankenstein-inspired class. If you think this is cheating by requiring the JDK, note that the Eclipse developers have managed to implement a Java compilation library on top of the JRE, which you could use here instead. There’s also the BCEL (Byte Code Engineering Library). Finally, if you know the methods a class should have beforehand (as was the case in the previous example), you can start with an interface and then rely only on reflection to do the rest for you, without a class loader or compilation. Again, this is too advanced to discuss in full here (in fact, this is already very deep in Java’s internals at this point). Finally, it’s interesting to know that some developers have applied this concept toward creating software solutions that allow you to change a Java program while it is running, without having to stop it. Look up “hot code replace” if you wish to learn more about this.

Behavioral Patterns

Behavioral patterns are concerned with organizing communication between objects in an efficient and clean manner.

Here, the chain-of-responsibility, observer (and model-view-controller), iterator, visitor, template, and strategy patterns are covered. The command, interpreter, mediator, and state patterns are not discussed as they are less commonly used. They are interesting and very useful in particular cases, but deal less with architecture-related object-oriented programs and are therefore less suitable for a beginner’s tour.

Chain-of-Responsibility Pattern

The chain-of-responsibility pattern describes a method that organizes objects in a chain. This is useful when certain commands are handled by different objects, each of them passing the command to the next object in the chain.

A simple example to illustrate this pattern is through a logging system. Imagine that an abstract Logger class is defined like so:

public abstract class Logger {

private Logger nextLoggerInChain;

public void setNext(Logger logger) {

nextLoggerInChain = logger;

}

public void logMessage(String message) {

performLogging(message);

// Pass command to next link in chain:

if (nextLoggerInChain != null)

nextLoggerInChain.logMessage(message);

}

// Method to be extended by subclasses

abstract protected void performLogging(String message);

}

Separate subclasses, such as ConsoleLogger, DatabaseLogger, FileLogger, and so on, log messages to a related target and pass the message to the next link in the chain. An example main method might look like this:

public static void main(String[] args) {

Logger consoleLogger = new ConsoleLogger();

Logger emailLogger = new EmailLogger();

Logger databaseLogger = new DatabaseLogger();

consoleLogger.setNext(emailLogger);

emailLogger.setNext(databaseLogger);

// Send a message down the chain:

consoleLogger.logMessage("Log this message");

}

The chain-of-responsibility pattern is somewhat underappreciated in practice. In most cases, you’ll see examples like this one being solved by an implementation of the observer pattern (which you’ll see next). However, the chain-of-responsibility pattern is still mentioned here, for two reasons. The first one is that this pattern also defines an order of responsibility. For a logging system, this is not important, but for other systems, it can be useful to establish an ordering, with the first objects in the chain getting a chance to handle the incoming message or object (and potentially modify it) before passing it on. The second reason entails this “passing on” behavior. In most cases where this pattern is useful, you’ll code it in such a way that an object in the chain will only pass on an incoming object when the current link is unable to do anything useful with it. Once a link that can handle the incoming object is found, the processing stops there.

Observer Pattern and Model-View-Controller Pattern

The observer pattern describes a framework in which a particular object (the subject) maintains a list of dependents (observers) and notifies them of any important changes.

In Java, this pattern can be implemented in different ways. The first one is by using Java’s built-in java.util.Observable class and the java.util.Observer interface. Observable defines the following methods your classes can override:

· void addObserver(Observer o)

· void deleteObserver(Observer o)

· void deleteObservers()

· int countObservers()

· protected void clearChanged()

· boolean hasChanged()

· void notifyObservers()

· void notifyObservers(Object arg)

· protected void setChanged()

The Observer interface defines only one method—void update(Observable o, Object arg)—which will be called by the subject. This is illustrated with an example. Say you are building an application to fetch stock quotes. At one point, you realize that other applications might want to plug in to your code to register an interest to be notified whenever you pull in a new stock quote. You decide to make the relevant class in your code Observable:

import java.util.Observable;

// Observable class (the subject)

public class StockTicker extends Observable {

public void updateStock() {

// Stock retrieving code here

// At the end, we get a Quote object, which we pass to our observers

Quote latestStockQuote = ...;

notifyObservers(latestStockQuote);

}

}

Note the notifyObservers call. Observers can now be implemented as a class like so:

import java.util.Observable;

import java.util.Observer;

public class StockObserver implements Observer {

@Override

public void update(Observable sender, Object receivedObject) {

Quote quote = (Quote) receivedObject;

// Do something interesting with the quote here

}

}

Before getting updates from the subject, observers have to register themselves, like so:

public static void main(String[] args) {

StockTicker ticker = new StockTicker();

StockObserver observer = new StockObserver();

ticker.addObserver(observer);

// Add other observers

}

The observer pattern encapsulates a simple but powerful concept that can greatly extend your programs. The observer pattern isn’t implemented by using java.util.Observable class and java.util.Observer in many real-life cases, but rather is implemented in the form of a hand-rolled approach where one class keeps a list of objects it needs to “notify” in some way whenever something interesting happens. The reasons for this stem mainly from the fact that programmers sometimes want to get creative with their use of notification methods, want to keep a list of multiple types of observers, or are hindered by the fact that Observable is an abstract class that needs to be extended, preventing classes from extending from another abstract class. Don’t worry, though, the pattern and the basic idea behind it are the same.

In fact, it seems that many built-in parts of Java are hampered by this issue, since many classes (especially those dealing with GUIs) prefer to go for a hand-rolled approach. In the Java world, people often refer to the observer pattern as the listener pattern (observers are called listeners in that case). The basic pattern is like this:

· The subject class keeps a list of registered listeners and is free to extend its own abstract classes.

· The listeners all implement an interface defining the “notification” methods.

· When something interesting happens to the subject, it goes through its list of listeners and calls the appropriate notification method.

A perfect way to illustrate this is by building a simple GUI application. As the subject, a simple JFrame is used here:

import javax.swing.JFrame;

public class GUIListenerExample {

public static void main(String[] args) {

// Create our subject

JFrame frame = new JFrame();

frame.setSize(400, 400);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Create our listener

MyMouseListener listener = new MyMouseListener();

// Register

frame.addMouseListener(listener);

// Go

frame.setVisible(true);

}

}

Note that javax.swing.JFrame extends java.awt.Frame and thus has nothing to do with Observable. The listener is created as follows:

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import javax.swing.JOptionPane;

public class MyMouseListener implements MouseListener {

@Override

public void mouseClicked(MouseEvent e) {

JOptionPane.showMessageDialog(null, "You clicked the mouse!");

}

@Override

public void mouseEntered(MouseEvent e) {}

@Override

public void mouseExited(MouseEvent e) {}

@Override

public void mousePressed(MouseEvent e) {}

@Override

public void mouseReleased(MouseEvent e) {}

}

This produces the mesmerizing application shown in Figure 12.5.

images

Figure 12.5

Note that nothing prevents you from implementing similar listener systems in your own applications. All you need is an interface and subjects that know how to register and notify them. Note that—as you’ve seen in the chapter dealing with GUI applications—you can also create one-shot throwaway listeners, like so:

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

public class GUIListenerExample {

public static void main(String[] args) {

// Create our subject

JFrame frame = new JFrame();

frame.setSize(400, 400);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Create our listener

MouseListener listener = new MouseListener(){

public void mouseClicked(MouseEvent e) {

JOptionPane.showMessageDialog(null, "You clicked the mouse!");

}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

};

// Register

frame.addMouseListener(listener);

// Go

frame.setVisible(true);

}

}

In some other cases, you’ll see the subject acting as its own listener (GUIListenerExample extends JFrame implements MouseListener):

import java.awt.event.MouseEvent;

import java.awt.event.MouseListener;

import javax.swing.JFrame;

import javax.swing.JOptionPane;

public class GUIListenerExample extends JFrame implements MouseListener {

public GUIListenerExample() {

this.addMouseListener(this);

}

public void mouseClicked(MouseEvent e) {

JOptionPane.showMessageDialog(null, "You clicked the mouse!");

}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

public static void main(String[] args) {

// Create our subject

JFrame frame = new GUIListenerExample();

frame.setSize(400, 400);

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Go

frame.setVisible(true);

}

}

Without a doubt, the observer pattern is one of the most useful patterns out there, especially since it forms the cornerstone of another pattern: model-view-controller. You were introduced to this pattern in the previous chapter on GUIs. Basically, a model-view-controller architecture expresses the fact that every program should be separated in three loosely coupled parts:

· The model as the central component containing the problem domain (accounts, users, and so on). This part should be completely independent from the user interface in the sense that the interface cannot directly manipulate the model.

· The controller is the component that sends commands to the model to manipulate it.

· The view is the interface through which the users get an output representation.

This description is a bit hand-wavy, so it is illustrated here with a simple example. Say you want to create a program to deal with checking accounts and you want to put a fancy GUI on top of it. You could start by building a simple window and just keep adding lists and variables until you have everything stuffed in a single ball of code spaghetti, but this is not the object-oriented way to do things. Instead, you start building the core problem domain and think about the classes that matter. In this case, that would be an account. You may recall this example from earlier:

public class Account {

private String name;

private double amount;

public Account(String name) {

this(name, 0);

}

public Account(String name, double startAmount) {

this.name = name;

this.amount = startAmount;

}

public boolean isOverdrawn() {

return this.amount < 0;

}

public void addFunds(double amount) {

this.amount += amount;

}

public String getName() {

return name;

}

public double getAmount() {

return amount;

}

}

Next up, you want to create a GUI to manage your account objects. You could start adding windows to this class, but again, this does not seem like the right way to do it. Instead, you decide to start with a new class:

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JTextField;

public class AccountWindow extends JFrame {

private JTextField funds, add;

private JButton addButton;

public AccountWindow() {

this.setSize(400, 120);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.setLayout(new FlowLayout());

funds = new JTextField(30);

add = new JTextField(30);

addButton = new JButton("Add funds");

funds.setEditable(false);

addButton.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent arg0) {

// Add funds here

}

});

this.add(funds);

this.add(add);

this.add(addButton);

}

}

The interface is Spartan-looking for now, but it will do, as shown in Figure 12.6.

images

Figure 12.6

A bigger question on your mind is how to fill in the “Add Funds” box. You want to keep your model and view as separate as possible, and thus avoid passing an Account object to this view. This is where the controller comes in. You start out by defining your controller:

import javax.swing.JFrame;

public class AccountController {

private Account model;

private AccountWindow view;

public AccountController(Account model, AccountWindow view) {

this.model = model;

this.view = view;

}

public void addFunds(double amount) {

model.addFunds(amount);

view.updateView(model.getAmount());

}

public static void main(String args[]) {

Account myAccount = new Account("AimeeBartSeppe Ltd.", 3000);

AccountWindow myView = new AccountWindow();

AccountController controller = new AccountController(myAccount, myView);

myView.setController(controller);

myView.updateView(myAccount.getAmount());

myView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

myView.setVisible(true);

}

}

And modify the view accordingly:

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JTextField;

public class AccountWindow extends JFrame {

private JTextField funds, add;

private JButton addButton;

private AccountController controller;

public AccountWindow() {

this.setSize(400, 120);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.setLayout(new FlowLayout());

funds = new JTextField(30);

add = new JTextField(30);

addButton = new JButton("Add funds");

funds.setEditable(false);

addButton.addActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent arg0) {

if (controller != null)

controller.addFunds(Double.parseDouble(add.getText()));

}

});

this.add(funds);

this.add(add);

this.add(addButton);

}

public void updateView(double funds) {

this.funds.setText("Your funds: "+funds);

this.add.setText("");

}

public void setController(AccountController controller) {

this.controller = controller;

}

}

You pat yourself on the back. Well done, the view can now call actions in the controller, which modifies its models and sends back updates to the view.

Still, you can’t help but feel there’s something missing . . . Most beginner programmers who want to decouple models and UI will end up with a solution resembling something you’ve just seen. Although the model is now completely unaware of the user interface that might exist (good!), the problem still remains that the controller is calling methods in the view and the view is calling methods in the controller, creating a problem where both need to be explicitly aware of one another.

Luckily, thanks to the observer pattern, there is a way to improve this situation. The basic idea is this: controllers will act as listeners in that they will be notified by the view and then can manipulate their models. The views will also act as listeners; they will be notified by the models but will not be able to manipulate them directly. The models finally act as subjects that notify interested parties, including the listening views.

One aspect of this idea has already been implemented, in a way. That is, the setController method basically registers the controller as a listener in the view. However, there is a smarter way to do this. Since you need to listen for UI events coming from the view, why not define the controller as an UI listener? Second, the controller is now allowed to call methods in the view, serving as an intermediator between the data stored in the models and how it is represented in the views. A better way to do this is to change your model to allow listeners.

So, start again with the Account class. You need to add the ability to notify view listeners. There are several ways to do this:

· A registerView method that just sets a single view listener.

· A hand-rolled listener solution that keeps a list of views.

· A hand-rolled listener solution that keeps a list of objects implementing a self-made AccountListener interface.

· Using the built-in Observable and Observer class and interface.

In order to make your program future-proof, go for the most flexible route. Start with defining an AccountListener interface:

public interface AccountListener {

public void notifyFundsChanged(double newAmount);

}

Next up, modify your model to accept AccountListeners:

import java.util.ArrayList;

import java.util.List;

public class Account {

private List<AccountListener> listeners;

private String name;

private double amount;

public Account(String name) {

this(name, 0);

}

public Account(String name, double startAmount) {

this.name = name;

this.amount = startAmount;

this.listeners = new ArrayList<>();

}

public boolean isOverdrawn() {

return this.amount < 0;

}

public void addFunds(double amount) {

this.amount += amount;

for (AccountListener listener : listeners)

listener.notifyFundsChanged(getAmount());

}

public String getName() {

return name;

}

public double getAmount() {

return amount;

}

public void addAccountListener(AccountListener listener) {

listeners.add(listener);

}

public void removeAccountListener(AccountListener listener) {

listeners.remove(listener);

}

public void removeAllAccountListeners() {

listeners.clear();

}

}

Next, change your view to behave as a listener. You also need to modify the view to accept listening controllers. In most cases, every view will have a single listening controller, so you can just keep this as a field instead of a list. Also, the controller object is used directly instead of defined as a separate listening interface (you can try to define an AccountViewListener and work from there if you want).

import java.awt.FlowLayout;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JTextField;

public class AccountWindow extends JFrame implements AccountListener,

ActionListener {

private JTextField funds, add;

private JButton addButton;

private AccountController controller;

public AccountWindow() {

this.setSize(400, 120);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

this.setLayout(new FlowLayout());

funds = new JTextField(30);

funds.setEditable(false);

add = new JTextField(30);

addButton = new JButton("Add funds");

addButton.addActionListener(this);

this.add(funds);

this.add(add);

this.add(addButton);

}

@Override

public void notifyFundsChanged(double newAmount) {

this.funds.setText("Your funds: "+newAmount);

this.add.setText("");

}

public void registerController(AccountController controller) {

this.controller = controller;

}

@Override

public void actionPerformed(ActionEvent e) {

controller.notifyAddFunds(Double.parseDouble(add.getText()));

}

}

Finally, your controller should be reworked:

import javax.swing.JFrame;

public class AccountController {

private Account model;

public AccountController(Account model, AccountWindow view) {

this.model = model;

}

public static void main(String args[]) {

Account myAccount = new Account("AimeeBartSeppe Ltd.", 3000);

AccountWindow myView = new AccountWindow();

AccountController controller = new AccountController(myAccount, myView);

// Register controller and view

// (This can also be done in controller constructor)

myView.registerController(controller);

myAccount.addAccountListener(myView);

myView.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

myView.setVisible(true);

}

public void notifyAddFunds(double amount) {

model.addFunds(amount);

}

}

Note that, technically, the controller does not need to be aware of the view anymore, so you can remove the view field from the controller class. You can also remove it from the constructor, but depending on your preference, you can also let the controller handle its registration and register the view in the model, so that you can decide to keep it in.

When you run this application, you’ll see that it behaves exactly the same as before.

NOTE Well. . . almost exactly. Notice that the funds field stays empty until you add some funds. How would you solve this issue while adhering to the model-view-controller principles? Purists would argue that it would be best to add annotifyControllerRegistered method to the controller that can be called by the view. The controller would then call a refreshListeners method in the model, which in turn would notify all the listening views to update their states. If this looks like a roundabout way of doing things, don’t feel too bad; some implementations prefer to pass the controller to a view’s constructor, so that it can be immediately registered and the controller can be notified immediately after the UI is set up. Some implementations allow views to be registered with controllers so that the controller can update their state directly, as you’ve seen before. In practice, all of these approaches are fine. The key reasoning behind the model-view-controller pattern is to establish a clear separation among data, data manipulation, and data representation. In other words, never mix UI code with data-handling code.

Before continuing with the next pattern, you may want to read the sidebar about the so-called “lapsed listener” problem.

LAPSED LISTENERS


The observer pattern is a very helpful one, but it comes with a tricky issue that can pose problems in larger applications and cause memory leaks with long-running applications (applications taking up more and more memory until they crash).

The problem is this: let’s say that you have a large application and the user decides to close the window of the account. The AccountWindow object is thrown away, and the user returns to an overview screen of all accounts. (By the way, if you feel up for it, try expanding the previous example to work with lists of accounts in a model-view-controller paradigm instead of just one account. Which classes do you need to add and which do you need to change?) So far so good, except that the window is still registered as a listener of the account object, which is still being kept around. This prevents the garbage collector from removing the window object from memory. In fact, the account object will just continue to notify the (hidden) window object for eternity. When an account is opened again in the UI, the programmer creates a new view object, so that over time, more and more objects are created and remain stuck as listeners.

The solution is simple: make sure to deregister all listeners when it is time to close them. Note that this is one of the few times when it makes sense to override Object.finalize():

@Override

public void finalize() {

// Deregister this listener

super.finalize(); // Perform rest of finalization

}

However, this is easier said than done, since a listener . . . listens. It cannot shout to its subjects, “Hey, I’m about to get deleted; please deregister me from your lists.” You need to make sure that this listener is deregistered when it’s destroyed. A good way to do this is to have the view send a notification to the controller (“I’ve been closed, goodbye . . .”) so that the controller can proceed to deregister the listener from the relevant views.

Another, more advanced, way of solving this issue is to work with so-called “weak references.” These specify a reference to an object (a listener), but allow the garbage collector to kick in once all normal references are gone. In Java, this functionality is provided by the WeakReference built-in class, but it is best to explicitly program deregistration in your code. After all, that is why removeListener methods are provided.

Iterator Pattern

The next pattern discussed is also built in to Java itself, although contrary to the observer pattern, the iterator pattern is much more useable.

The design goal behind an iterator is to traverse through and access a container’s elements, for instance, looping through members in a Java List object. Why is this concept so earth-shattering, you might ask. After all, if you have a collection, it makes sense that you can loop through its members. True, but the iterator pattern decouples the way a container is looped over from the container. For instance, the most logical iterator for a list would loop through members by index, but you might prefer to loop through members in reverse order, or random order, or sorted alphabetically first.

In Java, two interfaces exist to apply this pattern. Iterable is implemented by collections that can be looped over, and they subsequently implement a method (iterator()) to return an object implementing the Iterator interface. This is done with the methodshasNext(), next(), and remove().

All collections in Java extend Iterable, meaning that you can get an iterator from every collection class. The following examples illustrates how you can loop over a list using an iterator:

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

public class IteratorTest {

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("Bart");

list.add("Aimee");

list.add("Seppe");

Iterator<String> iterator = list.iterator();

while (iterator.hasNext()) {

String name = iterator.next();

System.out.println(name);

if (name.startsWith("A")) {

iterator.remove();

System.out.println(" ... removed");

}

}

}

}

What if you want to create a list returning a backward looping iterator? Just extend the built-in List class, extending the iterator() method to return your own iterator. If you want to create a list that can return both iterators, it’s easiest to extend the List class with a separate method, as is done in the following snippet:

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

public class IteratorTest {

public static void main(String[] args) {

BackwardsList<String> list = new BackwardsList<>();

list.add("Bart");

list.add("Aimee");

list.add("Seppe");

Iterator<String> iterator = list.getBackwardsIterator();

while (iterator.hasNext()) {

String name = iterator.next();

System.out.println(name);

if (name.startsWith("A")) {

iterator.remove();

System.out.println(" ... removed");

}

}

}

public static class BackwardsList<T> extends ArrayList<T> {

public Iterator<T> getBackwardsIterator() {

final List<T> arrayList = this;

Iterator<T> iterator = new Iterator<T>() {

private int currentIndex = arrayList.size()-1;

@Override

public boolean hasNext() {

return currentIndex >= 0;

}

@Override

public T next() {

return arrayList.get(currentIndex––);

}

@Override

public void remove() {

arrayList.remove(currentIndex+1);

}

};

return iterator;

}

}

}

Visitor Pattern

Just as with the chain-of-responsibility pattern you’ve seen before, the visitor pattern is a little bit more advanced and its benefits might not immediately be clear when programming smaller applications.

The main goal of a visitor pattern is to separate a certain algorithm (some code) from the data structure it operates on. Or, more practically speaking, the visitor pattern allows methods to be added to a related set of classes without having to modify the classes themselves.

Again, let’s dive immediately into an example. Let’s say you’re creating a book management system. You decide to start from an abstract class and extend it to some concrete classes:

public abstract class Book {

private String title;

// ...

}

public class NonFictionBook extends Book {

// ...

}

public class FictionBook extends Book {

// ...

}

public class AudioBook extends Book {

// ...

}

So far, so good. Now let’s say at some point you have a list of books you’re keeping track of:

public class VisitorTest {

public static void main(String[] args) {

Book[] books = new Book[]{

new AudioBook(), new FictionBook(), new FictionBook(),

new NonFictionBook(), new AudioBook(), new NonFictionBook()

};

}

}

Okay, now you want to sort this collection of books into three lists, one for each concrete class of books. Simple, you think; you can just write the following:

import java.util.ArrayList;

import java.util.List;

public class VisitorTest {

public static void main(String[] args) {

Book[] books = new Book[]{

new AudioBook(), new FictionBook(), new FictionBook(),

new NonFictionBook(), new AudioBook(), new NonFictionBook()

};

List<AudioBook> audioBooks = new ArrayList<>();

List<FictionBook> fictionBooks = new ArrayList<>();

List<NonFictionBook> nonFictionBooks = new ArrayList<>();

for (Book book : books) {

if (book instanceof AudioBook)

audioBooks.add((AudioBook) book);

else if(book instanceof FictionBook)

fictionBooks.add((FictionBook) book);

else if(book instanceof NonFictionBook)

nonFictionBooks.add((NonFictionBook) book);

}

System.out.println("Nr. audio books: "+audioBooks.size());

System.out.println("Nr. fiction books: "+fictionBooks.size());

System.out.println("Nr. non-fiction books: "+nonFictionBooks.size());

}

}

However, there are a number of problems with this code. It’s kind of ugly looking with all the type- casting going on, and you realize that adding new types of books will require you to do a search for all places where you do something like this.

Luckily, the visitor pattern can help you separate your algorithm (categorizing a list of books in separate lists) from the data structure (the abstract class and its dependents). How? First of all, you define an interface for a “visitor,” in this case, someone who can visit books like so:

public interface BookVisitor {

public void visit(AudioBook book);

public void visit(FictionBook book);

public void visit(NonFictionBook book);

}

Next, add a method to your Book class so that it can accept visitors, as well as to all the concrete classes:

public abstract class Book {

private String title;

// ...

abstract public void accept(BookVisitor visitor);

}

public class FictionBook extends Book {

@Override

public void accept(BookVisitor visitor) {

visitor.visit(this);

}

}

public class AudioBook extends Book {

@Override

public void accept(BookVisitor visitor) {

visitor.visit(this);

}

}

public class NonFictionBook extends Book {

@Override

public void accept(BookVisitor visitor) {

visitor.visit(this);

}

}

You might be thinking there is no difference between this and the previous strategy, since both require you to override the accept methods. However, since the concrete class extends the abstract class, Eclipse will throw a warning in case you forget to implement this method in your new concrete class, and consequently will throw a warning to implement this in your interface as well. In short, there’s no risk of forgetting anything in this case.

Finally, you need to define the visitor implementation:

import java.util.ArrayList;

import java.util.List;

public class CategorizingBookVisitor implements BookVisitor {

private List<AudioBook> audioBooks;

private List<FictionBook> fictionBooks;

private List<NonFictionBook> nonFictionBooks;

public CategorizingBookVisitor() {

this.audioBooks = new ArrayList<>();

this.fictionBooks = new ArrayList<>();

this.nonFictionBooks = new ArrayList<>();

}

@Override

public void visit(AudioBook book) {

audioBooks.add(book);

}

@Override

public void visit(FictionBook book) {

fictionBooks.add(book);

}

@Override

public void visit(NonFictionBook book) {

nonFictionBooks.add(book);

}

public List<AudioBook> getAudioBooks() {

return audioBooks;

}

public List<FictionBook> getFictionBooks() {

return fictionBooks;

}

public List<NonFictionBook> getNonFictionBooks() {

return nonFictionBooks;

}

}

The main program code now becomes a simple and elegant affair:

public class VisitorTest {

public static void main(String[] args) {

Book[] books = new Book[]{

new AudioBook(), new FictionBook(), new FictionBook(),

new NonFictionBook(), new AudioBook(), new NonFictionBook()

};

CategorizingBookVisitor visitor = new CategorizingBookVisitor();

for (Book book : books)

book.accept(visitor); // Accept the visitor

System.out.println("Nr. audio books: "+visitor.getAudioBooks().size());

System.out.println("Nr. fiction books: "+visitor.getFictionBooks().size());

System.out.println("Nr. non-fiction books:

"+visitor.getNonFictionBooks().size());

}

}

The workings of the visitor pattern should be pretty clear by now. Consider the visitor to be an object ringing a doorbell at another object’s house. By calling the accept method, the object opens the door and lets the visitor in.

Template Method Pattern

The last two patterns to be discussed are easy to understand and make you think, “Of course, this definitely seems like the normal way to do this.” Nevertheless, it is worthwhile to explicitly discuss them, because they are easy to overlook in practice if you haven’t encountered them before.

The template method pattern defines a general structure of a piece of code in a so-called template method, where some particular steps are kept abstract, left to the concrete subclasses to implement. This pattern is particularly useful when you know the general structure of a piece of code, but want to keep the internals flexible.

This can be illustrated with a simple example. Let’s say you’ve written some code to get a list of expenses your company has incurred from a database, which you store in a List<Expense> object. You know that management will want to run different kinds of statistics on this list of expenses. What is the total sum of all expenses? How many different receiving companies are involved? How many products of a certain type were purchased? And so on. In any case, you realize that the general structure of all these questions is the same: you loop through the list, do something with every expense, and present the result. As such, you could make this type of template method:

import java.util.List;

public abstract class ExpenseListCalculator {

public final double calculate(List<Expense> expenses) {

initialize();

for (Expense expense : expenses) {

handle(expense);

}

return getResult();

}

protected abstract void initialize();

protected abstract void handle(Expense expense);

protected abstract double getResult();

}

Calculating the total sum of all expenses would now look like this:

public class ExpenseTotalSumCalculator extends ExpenseListCalculator {

private double total;

@Override

protected void initialize() {

total = 0;

}

@Override

protected void handle(Expense expense) {

total += expense.getTotalPrice();

}

@Override

protected double getResult() {

return total;

}

}

Finding the amount of expense lines related to a given product is similarly easy:

public class ExpenseProductCountCalculator extends ExpenseListCalculator {

private double count;

private String product;

public ExpenseProductCountCalculator(String productToFind) {

this.product = productToFind;

}

@Override

protected void initialize() {

count = 0;

}

@Override

protected void handle(Expense expense) {

if (expense.getProduct().equals(product))

count++;

}

@Override

protected double getResult() {

return count;

}

}

The benefit of using this pattern comes not only from the fact that you can now easily create new implementations for a general algorithm, but you will also able to work with all these algorithms in a general manner, using the abstract class. You could now, for instance, easily define a list of ExpenseListCalculator objects that should be executed one by one and added to a report.

Strategy Pattern

The strategy pattern also deals with generalizing algorithms, but here, the focus is on selecting a particular algorithm at runtime, instead of algorithms extending a general template.

To see how this works, let’s say you have a customer class. You want to add a method in order to send notification messages to this user. Depending on the user’s preferences, they can be notified by text message, email, or Twitter. You want to avoid adding a bunch ofnotifySMS, notifyEmail, and notifyTwitter methods, which look ugly and are hard to maintain later on. There should be a better, object-oriented way to do this.

You realize that all these ways of notifying a customer can be regarded as different notification strategies, and thus get to work. First, you define a general interface:

public interface NotifyStrategy {

public void notify(Customer customer, String message);

}

The customer class looks something like this:

public class Customer {

private NotifyStrategy notificationPreference;

public Customer(NotifyStrategy notificationPreference) {

this.notificationPreference = notificationPreference;

}

// ...

public void notify (String message) {

notificationPreference.notify(this, message);

}

}

An implementation of the notification strategy then looks like this:

public class EmailNotifyStrategy implements NotifyStrategy {

@Override

public void notify(Customer customer, String message) {

// Send mail to customer.getEmail();

System.out.println("Sending an email: "+message);

}

}

You can test the program like so:

public class StrategyTest {

public static void main(String[] args) {

EmailNotifyStrategy emailNotifier = new EmailNotifyStrategy();

Customer a = new Customer(emailNotifier);

Customer b = new Customer(emailNotifier);

a.notify("Your product has shipped!");

}

}

This concludes the tour of the creational, structural, and behavioral design patterns. It is easy to feel overwhelmed by these concepts, especially if you’re a newbie. Some of these patterns might look confusing, and some of them might look so nicely designed that you want to use them right away. Be careful not to overuse them.

Most of the patterns that were discussed deal, ultimately, with abstracting concepts and algorithms into more classes and objects. Most programmers will go through an “abstraction vision quest” sometime in their career, looking roughly like this:

1. Beginner programmers don’t care too much about patterns and architecture. Their programs are small. They take care to put every concept into an appropriate class, but things stay manageable and nimble.

2. As programmers undertake bigger and bigger projects, they become at one point annoyed by a problem that one of the patterns tries to solve. Why do I have to keep adding subclasses to define product types? Maybe I should think at a higher level? What if I make a product type itself a class? Programmers start looking into design patterns, and suddenly, they seem like the perfect solution to all their problems.

3. Programmers are so happy with their use of patterns and the elegance they bring along, they start “thinking” ahead and start working out beautiful, elegant, and abstract architectures for their new projects.

4. At some point, everything breaks down again. Programmers undertake new projects with an inherent need to create decorators, observers, and some factories thrown in as well. At this point, you’ll see the dreaded interface-abstract-concrete class combo showing up for every concept that needs to be defined. It is a grandiose architecture, but somehow, the joy is lost.

5. And so, at some point, programmers try hard not to over-engineer everything. Yes, big software architectures are impressive, but for simple projects, simplicity also brings elegance.

The main takeaway is that you should keep things as simple as possible. What is important to remember is that you should always keep an eye out for things that can be improved as your projects grow. Starting out small is awesome to keep things moving along, but when it’s time to finally fix that hard-to-maintain issue, it’s nice to keep the things you’ve learned in this chapter in the back of your mind.

HELPFUL LIBRARIES

It’s impossible to list all libraries here, so the most common ones are included here for your reference. The main reason why we discuss these here—in a chapter on patterns and best practices—is because the Java ecosphere thrives mainly thanks to the great amount of excellent, well-known, and reliable libraries that have been built by the community throughout the years. Therefore, the old advice to “buy the best, build the rest” seems like a fitting inspiration (especially when “buy” means “get for free” in this context) for this closing best practice, which is to make use of helpful libraries. You might even end up loving some of them so much that you’ll include them by default on the build paths of all your projects.

Apache Commons

The Commons is an Apache open-source project dedicated to building reusable Java components. In short, you could describe this as the project that adds the missing batteries to Java.

In actuality, this project does not encompass one library, but is split into a series of packages. Not all of them are as useful, but the following ones are worth checking out:

· Apache Commons Collections: Provides a replacement for Java’s collection types. It is more efficient, more expandable, and easier to garbage collect.

· Apache Commons CSV: Useful when dealing with CSV files.

· Apache Commons Exec: Useful when you want to start and manage other programs from Java.

· Apache Commons IO: By far the most widely used commons library, mainly due to its FileUtils utility class, which adds a lot of static utility functions to deal with common file handling operations in a sensible manner. One downside is that, for compatibility reasons, File objects are used everywhere, instead of the NIO Path.

· Apache Commons Lang: Adds functionality to Java’s language core. Useful additions include a better Math class and better String operation methods.

· Apache Commons Logging: Adds a thin bridge between your program and many other logging libraries.

· Apache Commons Math: Adds many mathematical and statistics components not available in Java by default.

See http://commons.apache.org/.

Google Guava

Another collection of libraries, Guava is used by Google in all its Java projects. The most important features include:

· Replacement for Java’s default collections, making them faster and better. This was the initial idea for the creation of Guava, back when it was called “Google-collections” (you’ll see this library popping up, but it is outdated and replaced by Guava).

· Tools to avoid NullPointerExceptions.

· Tools to add preconditions to methods.

· Tools to simplify other common tasks, such as writing a toString() method.

· Tools to efficiently manipulate strings.

· Tools to deal with I/O.

· Mathematical library.

· Tools to write parallel programs.

Note that there is a high degree of overlap regarding the objectives and content of Google Guava and Apache Commons, so it’s best to stick with the same project if you can.

See https://code.google.com/p/guava-libraries/.

Trove

The Trove library provides yet another high-speed replacement for collection types. The reason for so many implementations stems from the fact that collections often form the cornerstone of every algorithm in Java, and it thus makes sense to keep these nimble and fast.

Trove is popular in some areas (some computer science researchers prefer it), but if you’re already using Apache Commons or Google Guava, there’s no need to add Trove for any particular reason. Just use the Commons Collections or Guava Collections.

See http://trove.starlight-systems.com/.

Colt

This is another library used by computer scientists aiming to add high-performance scientific computing functionality (linear algebra, statistics, fast multidimensional arrays, and others). In most cases, the functionality offered by Commons Math or Guava’s math classes should suffice, but you might see this library pop up in a number of very intensive applications.

See http://acs.lbl.gov/ACSSoftware/colt/.

Lombok

Lombok aims to provide a set of features that simplifies the development of Java programs. It includes functionality to avoid having to write getters and setters manually, or to create a “data” object in a very straightforward manner. Its website shows the following example:

import lombok.AccessLevel;

import lombok.Setter;

import lombok.Data;

@Data public class DataExample {

private final String name;

@Setter(AccessLevel.PACKAGE) private int age;

private double score;

private String[] tags;

}

This code will automatically create getters, setters, equals, hashCode, and toString.

See http://projectlombok.org/.

OpenCSV

Just like the Apache Commons CSV library (see “Apache Commons” above), this is another well-maintained library for dealing with CSV files. Using this library can be more ideal in projects where you only desire to add functionality to read and write CSV files, without requiring any other libraries from the Apache Commons project. Including the OpenCSV library is then a simple matter of including one JAR file in your build path.

Reading a CSV files then becomes as easy as writing the following:

// Read csvfile using comma separator (,), double quote quote character ("),

// and skip the first 1 line(s)

CSVReader reader = new CSVReader(new FileReader("csvfile.csv"), ',', '"', 1);

String [] nextLine;

while ((nextLine = reader.readNext()) != null) {

// Do something with nextLine here

}

See http://opencsv.sourceforge.net/.

HTML and JSON Libraries

These are libraries to parse HTML and deal with JSON data. Refer back to Chapter 10 of this book, which deals with web sources.

See the following libraries:

· http://jsoup.org/ (jsoup)

· http://jackson.codehaus.org/ (Jackson)

· https://code.google.com/p/google-gson/ (Google-gson)

Hibernate and Other JPA-Compliant Libraries

These are libraries that work with persistent data. See Chapter 9 of this book on databases.

See http://hibernate.org/ (Hibernate).

Joda-Time

This is a library that essentially fixes Java’s date and time classes. It is extremely useful if you’re stuck with an older Java version.

The library was so well written that the author of Joda-Time was contacted by Oracle to lead the development of the Java 8 java.time classes. So make sure you use those instead of java.util.Date when you can.

In case you cannot yet use Java 8 in one of your projects, see http://www.joda.org/joda-time/ to download the library.

Charting Libraries

Many charting libraries exist in Java for making 2D and 3D plots. They differ in terms of flexibility, ease-of-use, and license used.

See the following:

· http://www.jfree.org/jfreechart/ (JFreeChart)

· https://code.google.com/p/charts4j/ (charts4j)

· http://trac.erichseifert.de/gral/ (GRAL)

· http://freecode.com/projects/jchartlib (JChartLib)

· http://jzy3d.org/ (Jzy3d)

· http://jchart2d.sourceforge.net/ (JChart2D)

· http://docs.oracle.com/javase/8/javafx/user-interface-tutorial/charts.htm#JFXUI577 (charts with JavaFX)

3D Graphics Libraries

These libraries provide bindings from Java to OpenGL and related APIs to use hardware-accelerated graphics in your projects—useful if you’re developing graphic-intensive programs.

See the following:

· http://jogamp.org/ (JogAmp)

· http://www.lwjgl.org/ (LWJGL, geared toward game engine development)

· http://env3d.org/beta/ (Env3D)

· http://www.jpct.net/ (jPCT)

· http://www.oracle.com/technetwork/java/javase/tech/index-jsp-138252.html (Java 3D, which is outdated; replaced by JavaFX at this point)

· http://docs.oracle.com/javase/8/javase-clienttechnologies.htm (JavaFX)

Financial Libraries

These libraries offer financial quantitative algorithms and trading algorithms.

See the following

· http://www.jquantlib.org/en/latest/ (JQuantLib, derived from QuantLib—http://quantlib.org/index.shtml—which also offers bindings to Java but is written in C++.)

· http://finmath.net/java/ (FinMath)

· https://code.google.com/p/maygard/ (Maygard)

· http://www.javaquant.net/finalgo.html (Java Quant)

· http://www.eclipsetrader.org/ (EclipseTrader)