Working With Design Patterns: Odds and Ends, Page 2
A specification implementation provides the selection criteria in the isSatisfiedBy method:
public class AuthorSpecification implements Specification {
private final String author;
public AuthorSpecification(String author) {
this.author = author;
}
@Override
public boolean isSatisfiedBy(Holding holding) {
return author.equals(holding.getBook().getAuthor());
}
}
Here's another specification implementation that supports selecting by matching book title:
public class TitleSpecification implements Specification {
private final String title;
public TitleSpecification(String title) {
this.title = title;
}
@Override
public boolean isSatisfiedBy(Holding holding) {
return title.equals(holding.getBook().getTitle());
}
}
You conceivably could eliminate a bit of redundancy by factoring construction of both of these specifications into a common superclass.
With the specification classes in place, the code in HoldingsAccess simplifies. The single method findBy covers any specification passed to it:
public List<Holding> findBy(Specification specification) {
List<Holding> results = new ArrayList<Holding>();
for (Holding holding: getAll())
if (specification.isSatisfiedBy(holding))
results.add(holding);
return results;
}
The HoldingsAccess class is now closed to changes in Holding—adding a new Holding field now requires adding a new Specification implementation. The elimination of impact to HoldingsAccess meets the open-closed principle: Modules should be closed for modification, but open to extension.
Lazy Initialization
Lazy initialization means that you defer initializing a field to a useful value until the time it's first needed. It is a performance optimization.
A library system search returns a list of Book objects. The system presents the user with a list displaying basic book information—author, title, year published, and so on. When the user selects a book for further information, the system shows an image of the front cover of the book. Listing 2 shows an implementation of a Book class that handles loading an image upon construction.
Listing 2: Book.
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
public class Book {
private final String author;
private final String title;
private final String classification;
private final String year;
private BufferedImage coverImage;
public Book(String author, String title, String classification,
String year, String imageFilename) {
this.author = author;
this.title = title;
this.classification = classification;
this.year = year;
loadCoverImage(imageFilename);
}
public String getClassification() {
return classification;
}
public String getAuthor() {
return author;
}
public String getTitle() {
return title;
}
public String getYear() {
return year;
}
@Override
public String toString() {
return author + " " + title + " " + year + " " +
classification;
}
public BufferedImage getCoverImage() {
return coverImage;
}
private void loadCoverImage(String imageFilename) {
try {
coverImage = ImageIO.read(new File(imageFilename));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
