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