Exercise: Implementing Comparator Classes
Using Builder and Singleton Design Patterns
The Book Class
package book;
import java.util.Comparator;
publicclass Book {
private String title;
private String author;
private String publisher;
privateintpages;
privatelongisbn;
privatedoubleprice;
public Book(BookBuilder builder) {
title = builder.title;
author = builder.author;
publisher = builder.publisher;
pages = builder.pages;
isbn = builder.isbn;
price = builder.price;
}
@Deprecated// @Deprecated annotation discourages use of the method, but still allows it to function.
publicBook(String title, String author, String publisher, int pages, long isbn, double price) {
this.title = title;
this.author = author;
this.publisher = publisher;
this.pages = pages;
this.isbn = isbn;
this.price = price;
}
@Override// @Override annotation checks spelling and argument matching with superclass version
public String toString() {
return String.format("Title:%-24s Author:%-15s Publisher:%-10s Pages:%4d ISBN:%013d Price:%6.2f",title, author, publisher, pages, isbn, price);
}
// SINGLETON DESIGN PATTERN
// Comparator interface ensures that each inner static class implements a compare() method.
// The Collections.sort() method needs access to the specific compare() method.
// Java won't allow sending a reference to a method, but it will allow a reference to an object.
// Create an object that can access a compare() method; capture object location as a static final reference.
// Use the static final reference when calling Collections.sort().
// The Colletions.sort() algorithm can access the compare() method using the object reference.
publicstaticclass CompareTitle implements Comparator<Book> {
// No CompareTitle object will be created if instance variable never used.
publicfinalstatic CompareTitle instance = new CompareTitle(); // final static required to be SINGLETON
private CompareTitle() { } // private keyword prevents creation of CompareTitle objects outside the class
@Override// Comparator interface enforces requirement that a compare() method be implemented.
publicint compare(Book book1, Book book2){return book1.title.compareTo(book2.title);}
} // end class CompareTitle
publicstaticclassCompareAuthorimplements Comparator<Book> {
// TODO Your class that implements the Comparator interface will implement compare() using sAuthor
} // end class CompareAuthor
publicstaticclassComparePagesimplements Comparator<Book> {
// TODO Your class that implements the Comparator interface will implement compare() using nPages
} // end class ComparePages
publicstaticclassCompareISBNimplements Comparator<Book> {
// TODO Your class that implements the Comparator interface will implement compare() using lISBN
} // end class CompareISBN
publicstaticclassComparePriceimplements Comparator<Book> {
// TODO Your class that implements the Comparator interface will implement compare() using dPrice
} // end class ComparePrice
} // end class Book
The BookBuilder Class
package book;
// BUILDER DESIGN PATTERN
// Ordinary constructors might have long lists of unnamed arguments. The meaning of each argument may
// not be evident. The BUILDER design pattern creates a mechanism of implementing named arguments. It is
// an increasingly common style in the Java API . . . especially JavaFX.
publicclass BookBuilder {
// the absence of access specifiers ("public" "private" "protected") means "package" access.
// class Book is in the same package, and therefore has access to the instance fields of this class.
String title = "";
String author = "";
String publisher = "";
intpages;
longisbn;
doubleprice;
private BookBuilder(){}
publicstatic BookBuilder create() { returnnew BookBuilder(); }
// causes "new BookBuilder()" to return an object of type BookBuilder
// Named methods provide optional initialization. Returning "this" allows for cascading initialization calls.
public BookBuilder title(String title){ this.title = title;returnthis;}
public BookBuilder author(String author){this.author = author;returnthis;}
public BookBuilder publisher(String publisher){this.publisher = publisher;returnthis;}
public BookBuilder pages(int pages){this.pages = pages;returnthis;}
public BookBuilder isbn(long isbn){this.isbn = isbn;returnthis;}
public BookBuilder price(double price){this.price = price;returnthis;}
// build() creates a Book object, populates it with values already initialized by this BookBuilder object.
// BookBuilder object is sent to a newly created Book() object.
// The BookBuilder has already fully organized initialization.
public Book build(){ returnnew Book(this); }
} // end class BookBuilder
The TestBook Class
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import book.*;// imports both classes
publicclassTestBook {
publicstaticvoidmain(String[] args) {
// Uses the builder-pattern to create new Book objects. Only selected values need be initialized.
// Unknown values are skipped.
Book bookTheBelgariad = BookBuilder.create().title("The Belgariad")
.author("Eddings, David").pages(1823).isbn(144056789000L).price(99.99).build();
Book bookMasterCPlusPlus = BookBuilder.create().title("Master C++").author("Woollard, Rex").build();
Book bookWebstersDictionary = BookBuilder.create().title("Webster's Dictionary").build();
Book bookTheBible = BookBuilder.create().title("The Bible").isbn(1).build();
// When building multiple Book objects with some common values, re-use a single BookBuilder object
// In this case, the single BookBuilder object is populated with author and publisher
BookBuilder rowlingsBookBuilder = BookBuilder.create()
.author("Rowlings, J.K.").publisher("Bloomsbury").price(34.95);
// use the single BookBuilder object repeatedly to create Book objects which have only minor variations
Book bookPhilosophersStone = rowlingsBookBuilder.title("The Philosopher's Stone").build();
Book bookChamberOfSecrets = rowlingsBookBuilder.title("The Chamber of Secrets").pages(345).build();
Book bookPrisonerOfAzkaban = rowlingsBookBuilder.title("The Prisoner of Azkaban").build();
BookBuilder tolkeinBookBuilder = BookBuilder.create().author("Tolkein, J.R.R.").publisher("Penguin");
Book bookLordOfTheRings = tolkeinBookBuilder
.title("Lord of the Rings").pages(1178).isbn(123456789012L).price(123.45).build();
Book bookTheHobbit = tolkeinBookBuilder.title("The Hobbit").isbn(100056789000L).price(98.76).build();
// Uses the constructor-style to create new Book objects. All arguments must be present, whether or
// not they are needed. The Book constructor has been annotated with @Deprecated as a
// mechanism to discourage its use, but retain the functionality.
Book bookTheMallorean = newBook("The Mallorean", "Eddings, David", "New Line", 1940,
146758906640L, 88.88);
// don't know page count, or isbn number but must enter a value
Book bookGobletOfFire = newBook("The Goblet of Fire", "Rowlings, J.K.", "Bloomsbury", 0, 0L, 34.95);
Book bookOrderOfThePhoenix = newBook("Order of the Phoenix", "Rowlings, J.K.", "Bloomsbury", 0,
0L, 39.95);
Book bookHalfBloodPrince = newBook("Half Blood Prince", "Rowlings, J.K.", "Bloomsbury", 823,
0L, 37.95);
Book bookDeathlyHallows = newBook("Deathly Hallows", "Rowlings, J.K.", "Bloomsbury", 798,
0L, 42.95);
// store all books in a List Collection which can be sorted.
// The Book objects could have been created directly using the listBooks.add() method call,
// but were created independently for greater clarity in this example
List<Book> listBooks = newArrayList<Book>();
listBooks.add(bookTheHobbit);
listBooks.add(bookLordOfTheRings);
listBooks.add(bookTheBelgariad);
listBooks.add(bookTheMallorean);
listBooks.add(bookPhilosophersStone);
listBooks.add(bookChamberOfSecrets);
listBooks.add(bookPrisonerOfAzkaban);
listBooks.add(bookGobletOfFire);
listBooks.add(bookOrderOfThePhoenix);
listBooks.add(bookHalfBloodPrince);
listBooks.add(bookDeathlyHallows);
listBooks.add(bookMasterCPlusPlus);
listBooks.add(bookWebstersDictionary);
listBooks.add(bookTheBible);
System.out.println("Original Order");
for (Book book : listBooks)
System.out.println(book);
// Sort using Title as the key
// sort() is a static method in the Collections class; encapsulates a high-performance sorting algorithm.
Collections.sort(listBooks, Book.CompareTitle.instance);
System.out.println("\nSorted Order: Title");
for (Book book : listBooks)
System.out.println(book);
// Sort using Author as the key
Collections.sort(listBooks, Book.CompareAuthor.instance);
System.out.println("\nSorted Order: Author");
for (Book book : listBooks)
System.out.println(book);
// Sort using ISBN as the key
Collections.sort(listBooks, Book.CompareISBN.instance);
System.out.println("\nSorted Order: ISBN");
for (Book book : listBooks)
System.out.println(book);
// Sort using Price as the key
Collections.sort(listBooks, Book.ComparePrice.instance);
System.out.println("\nSorted Order: Price");
for (Book book : listBooks)
System.out.println(book);
// Sort using Pages as the key
Collections.sort(listBooks, Book.ComparePages.instance);
System.out.println("\nSorted Order: Pages");
for (Book book : listBooks)
System.out.println(book);
} // end main()
} // end class TestBook