Creation Patterns

This lab uses the following patterns:

Factory Method, Interning, Abstract Factory, Builder, Singleton

Command Factory

Following the Command Processor Pattern, we can view buttons, menu items, and other controls as command factories.

In this lab a simple control panel has a single button:

Clicking the button displays a simple dialog box:

Here's a partial design:

Here's a typical interaction:

Here's a partial implementation:

PanelViewer.java

Finish the application by creating two concrete commands and their corresponding concrete command factories. Executing either command displays a dialog box, but containing different messages.

Hint: The simple way to make a dialog box appear in Java:

JOptionPane.showMessageDialog(null, "message", "title", JOptionPane.INFORMATION_MESSAGE);

Slave Factory

An interpreter perpetually prompts the user for a command, executes the command, then displays the result. Unfortunately, there's no way for a user to cancel a command once it has been given. This might be convenient, for example, if the command caused an infinite loop or was taking too long to finish.

To overcome this problem, an interpreter can create a slave thread to execute a command. While the slave is executing the command, the interpreter can resume listening for more user inputs, including the "cancel" command, which causes the interpreter to stop all running slaves.

Create a reusable Interpreter framework that supports the cancel command. Supply the framework with a factory method for creating slaves.

Here's the interpreter's basic control loop:

while(true) {
   System.out.print("-> ");
   String cmmd = scanner.nextLine();
   if (cmmd.equals("quit")) {
      killSlaves();
      break;
   } else if (cmmd.equals("cancel")) {
      killSlaves();
      System.out.println("all slaves stopped");
   } else {
      Slave slave = makeSlave(cmmd);
      slavePool.add(slave);
      slave.start();
      System.out.println("running");
   }
}

To test your framework, create a customization that interprets the following commands:

isPrime n         // = true if n is prime
isSquare n        // = true if n is k^2 for some k
isSumOfSquares n  // = true if n is a^2 + b^2 for some a && b
isBiggestPrime n  // = true if n is the biggest prime

Utility Classes versus Singletons

All of the methods and fields of a utility class are static. There is no point in creating instances of a utility class. (In fact, we could prevent users from creating instances by declaring the class to be abstract.) For example, Java's Math class is a utility class containing standard mathematical functions:

class Math {
   public static double PI = ...;
   public static double E = ...;
   public static double sin(double x) { ... }
   public static double cos(double x) { ... }
   public static double log(double x) { ... }
   // etc.
}

While no instances of a utility class are created, only one instance of a singleton class can be created. Often a developer can choose between a utility class and a singleton class.

For example, a white page server contains a hash table that associates server names to server locations. The white page server provides a method that allows servers to register their names and locations, and provides a method that allows clients to locate servers using only their names. Of course all servers and clients will somehow need to know where the white page server is located. It might be confusing if an application has more than one white page server.

Here are the possible designs. First using Singleton:

sing

Second, using a utility class:

sing

UML note: An underlined method or field is static.

(a) Sketch an implementation of WhitePageServer, Server, and Client two times. In both cases your sketch should show how the client and server use the white page server. First implement WhitePageServer as a utility class, and then as a singleton class.

(b) Can you think of any advantages or disadvantages of singleton over utility or vice-versa?

Interning Value Objects

Instances of Java's Integer, Float, Double, etc. are good examples of value objects (state = identity). However, Java allows many instances of Integer to represent the same value:

Integer val1 = new Integer(42);
Integer val2 = new Integer(42);
System.out.println(val1 == val2); // false, identities differ
System.out.println(val1.equals(val2)); // true, values same

Create an Int class that, like Java's Integer class, wraps an int.

class Int {
   private int val;
   // etc.
}

Unlike Integer, Int uses the Interning pattern to prevent the creation of multiple value objects with the same value. As a diagnostic tool, the Int class should print out the number of instances it has each time a new Int is created.

Create a calculator class that provides static methods for arithmetically combining Ints:

class IntCalculator {
   public static Int add(Int arg1, Int arg2) { ... }
   public static Int sub(Int arg1, Int arg2) { ... }
   public static Int mul(Int arg1, Int arg2) { ... }
   public static Int div(Int arg1, Int arg2) { ... }
   public static Int rem(Int arg1, Int arg2) { ... }
}

Create a test driver that perpetually prompts the user for arithmetic commands, executes them using the above Calculator, then displays the result. Here's a sample output:

-> add 3 4
num ints = 1
num ints = 2
num ints = 3
7
-> add 3 7
num ints = 4
10
-> quit
bye

Note that in the second add command only one new Int was created: 10.

Diagnostic Tool

A test consists of many multiple-choice questions:

Running a test involves asking each question, reading the user's response, and adding the value of the question to the total points if the response is correct.

Asking a question involves displaying the question followed by a numbered list of possible responses.

A test driver creates a test, runs it, then prints the total score over the max score.

Examples

A History Test

Who started the Reformation?
select a response from the following:
(0)Ali G
(1)Marx
(2)Luther
(3)Joker
(4)Obama
your selection: 2
Good
+++++++++++++++++
Who stabbed Julius Ceasar?
select a response from the following:
(0)Ali G
(1)Brutus
(2)Himself
(3)M&M
(4)Cheney
your selection: 4
Sorry, correct response = 1
+++++++++++++++++
Year Louis 14th died?
select a response from the following:
(0)1715
(1)1235
(2)1863
(3)35
(4)1492
your selection: 0
Good
+++++++++++++++++
Civilization conquered by Cortez?
select a response from the following:
(0)Berkeley
(1)Spain
(2)Aztec
(3)Mayan
(4)Atlantis
your selection: 4
Sorry, correct response = 2
+++++++++++++++++
done!
your score = 20/40

A Math Test

6 * 7?
select a response from the following:
(0)23
(1)68
(2)42
(3)6
(4)2a
your selection: 2
Good
+++++++++++++++++
1 + 2 + ... + 10?
select a response from the following:
(0)23
(1)55
(2)42
(3)6
(4)2a
your selection: 1
Good
+++++++++++++++++
e^(i * pi)?
select a response from the following:
(0)23
(1)68
(2)42
(3)-1
(4)2a
your selection: 3
Good
+++++++++++++++++
last twin prime?
select a response from the following
(0)3,5
(1)17, 19
(2)34, 36
(3)41, 43
(4)unknown
your selection: 4
Good
+++++++++++++++++
gcd(36, 24)?
select a response from the following:
(0)12
(1)68
(2)42
(3)6
(4)2a
your selection: 0
Good
+++++++++++++++++
done!
your score = 50/50

Abstract Factory

We would like the logic of creating and running a test to be independent of the specific questions. Show how you could use the Abstract Factory Pattern to achieve this.

Hint: Compete the following:

class Test {
   public Test(TestFactory factory) { ??? }
   // etc.
}

Where:

interface TestFactory {
   int getNumQuestions();
   String makeQuestion(int pos);
   String[] makeResponses(int pos);
   int getValue(int pos);
   int getCorrectResponse(int pos);
}

(Note: pos = the position of the question in the test: #0, #1, #2, etc.)

Test your solution by completing the following:

class MathTestFactory implements TestFactory { ??? }

class TestDriver {
   public static void main(String[] args) {
      Test t = new Test(new MathTestFactory());
      t.run();
      System.out.println(
         "your score = " + t.getTotal() + "/" + t.getMax());
   }
}
  

Builder

Without changing Test or Question, redo the previous question this time using the Builder Pattern. In this case TestDriver plays the role of a directory that is provided with a concrete subclass of:

abstract class TestBuilder {
   protected Test test = new Test();
   public Test getTest() { return test; }
   public abstract void addQuestion(int pos);
   public abstract void addResponses(int pos);
   public abstract void addCorrectResponse(int pos);
}

(Note: pos = the position of the question in the test: #0, #1, #2, etc.)