Java Patterns

Sang Shin, sang.shin@sun.com, Sun Microsystems, www.javapassion.com/javaintro


This hands-on lab takes you through the basic Java patterns.


Expected duration: 120 minutes


Software Needed

Before you begin, you need to install the following software on your computer. 


Change Log


Lab Exercises


Exercise 1: Singleton pattern


In this exercise, you will learn how to write and use a singleton class.
  1. Write a singleton class

(1.1) Write singleton class


0. Start NetBeans IDE if you have not done so yet.
1. Create a new NetBeans project
2. Modify the IDE generated SingletonPattern.java as shown in Code-1.11 below.  Study the code by paying special attention to the bold fonted parts. 

public class SingletonPattern {
   
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        System.out.println("Calling a method of a Singleton: " + s1.demoMethod());
       
        Singleton s2 = Singleton.getInstance();
        System.out.println("Calling a method of a Singleton: " + s2.demoMethod());
       
        boolean b1 = (s1 == s2);
        System.out.println("Object instance s1 and s2 are the same object: " + b1);
       
    }

}
Code-1.11: SingletonPattern.java

3. Write Singleton.java as shown in Code-1.12 below.

public class Singleton {
   
    private static Singleton singleton = new Singleton();
   
    /** A private Constructor prevents any other class from instantiating. */
    private Singleton() {
    }
   
    /** Static 'instance' method */
    public static Singleton getInstance() {
        return singleton;
    }
   
    // other methods protected by singleton-ness would be here...
   
    /** A simple demo method */
    public String demoMethod() {
        return "demo";
    }
   
}
Code-1.12: Singleton.java

4. Build and run the project
Calling a method of a Singleton: demo
Calling a method of a Singleton: demo
Object instance s1 and s2 are the same object: true
Figure-1.13: Result of running SingletonPattern application

Solution: This exercise up to this point is provided as a ready-to-open-and-run NetBeans project as part of hands-on lab zip file. You can find it as <LAB_UNZIPPED_DIRECTORY>/javapatterns/samples/SingletonPatternYou can just open it and run it. 

                                                                                                              return to top of the exercise

Summary


In this exercise, you have learned how to write and use a singleton class.


                                                                                                                  return to the top


Exercise 2: FactoryMethod pattern

In this exercise, you are going to learn how to write a Java program using FactoryMethod pattern.

  1. FactoryMethod pattern example

(2.1) FactoryMethod pattern example


1. Create a new NetBeans project
2. Modify the IDE generated FactoryMethodPattern.java as shown in Code-2.11 below.  Study the code by paying special attention to the bold fonted parts. 

public class FactoryMethodPattern {
   
    public static void main(String [] arguments){
       
        System.out.println("Example for the FactoryMethod pattern - Creating a GUI editor for the Contact\n");
       
        System.out.println("The GUI defined in the EditorGui class is a truly generic editor.");
        System.out.println("It accepts an argument of type ItemEditor, and delegates");
        System.out.println(" all editing tasks to its ItemEditorInterface and the associated GUI.");
        System.out.println(" The getEditor() Factory Method is used to obtain the ItemEditor");
        System.out.println(" for the example.");
        System.out.println();
        System.out.println("Notice that the editor in the top portion of the GUI is,");
        System.out.println(" in fact, returned by the ItemEditor belonging to the");
        System.out.println(" ContactEditableImpl class, and has appropriate fields for that class.\n");
       
        // Create ContactEditableImpl object
        System.out.println("Creating a Contact object.\n");
        ContactEditableImpl someone = new ContactEditableImpl();
       
        EditorGui editorGui = new EditorGui(someone.getEditor());
        editorGui.createGui();  
       
    }
}
Code-2.11: FactoryMethodPattern.java

3. Write ContactEditableImpl.java as shown in Code-2.12 below.

import java.awt.GridLayout;
import java.io.Serializable;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

class ContactEditableImpl implements EditableInterface, Serializable {
   
    private String name;
    private String relationship;
   
    // Factory method
    public ItemEditorInterface getEditor() {
        return new ContactEditorItemEditorImpl();
    }
   
    private class ContactEditorItemEditorImpl implements ItemEditorInterface, Serializable {
        private transient JPanel panel;
        private transient JTextField nameField;
        private transient JTextField relationField;
       
        public JComponent getGUI() {
            if (panel == null) {
                panel = new JPanel();
                nameField = new JTextField(name);
                relationField = new JTextField(relationship);
                panel.setLayout(new GridLayout(2,2));
                panel.add(new JLabel("Name:"));
                panel.add(nameField);
                panel.add(new JLabel("Relationship:"));
                panel.add(relationField);
            } else {
                nameField.setText(name);
                relationField.setText(relationship);
            }
            return panel;
        }
       
        public void commitChanges() {
            if (panel != null) {
                name = nameField.getText();
                relationship = relationField.getText();
            }
        }
       
        public String toString(){
            return "\nContact:\n" +
                    "    Name: " + name + "\n" +
                    "    Relationship: " + relationship;
        }
    }
}
Code-2.12: ContactEditableImpl.java

4. Write EditableInterface.java as shown in Code-2.12 below.

interface EditableInterface {
    public ItemEditorInterface getEditor();
}
Code-2.12: EditableInterface.java

5. Write ItemEditorInterface.java as shown in Code-2.12 below.

import javax.swing.JComponent;

interface ItemEditorInterface {
    public JComponent getGUI();
    public void commitChanges();
}
Code-2.12: ItemEditorInterface.java

6. Write EditorGui.java as shown in Code-2.12 below.

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;

class EditorGui implements ActionListener{
   
    private JFrame mainFrame;
    private JTextArea display;
    private JButton update, exit;
    private JPanel controlPanel, displayPanel, editorPanel;
   
    // ItemEditorInterface field
    private ItemEditorInterface editor;
   
    public EditorGui(ItemEditorInterface edit){
        editor = edit;
    }
   
    public void createGui(){
        mainFrame = new JFrame("Factory Pattern Example");
        Container content = mainFrame.getContentPane();
        content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
       
        editorPanel = new JPanel();
        editorPanel.add(editor.getGUI());
        content.add(editorPanel);
       
        displayPanel = new JPanel();
        display = new JTextArea(10, 40);
        display.setEditable(false);
        displayPanel.add(display);
        content.add(displayPanel);
       
        controlPanel = new JPanel();
        update = new JButton("Update Item");
        exit = new JButton("Exit");
        controlPanel.add(update);
        controlPanel.add(exit);
        content.add(controlPanel);
       
        update.addActionListener(this);
        exit.addActionListener(this);
       
        mainFrame.addWindowListener(new WindowCloseManager());
        mainFrame.pack();
        mainFrame.setVisible(true);
    }
   
   
    public void actionPerformed(ActionEvent evt){
        Object originator = evt.getSource();
        if (originator == update){
            updateItem();
        } else if (originator == exit){
            exitApplication();
        }
    }
   
    private class WindowCloseManager extends WindowAdapter{
        public void windowClosing(WindowEvent evt){
            exitApplication();
        }
    }
   
    private void updateItem(){
        editor.commitChanges();
        display.setText(editor.toString());
    }
   
    private void exitApplication(){
        System.exit(0);
    }
}
Code-2.12: ItemEditorInterface.java

7. Build and run the project
Example for the FactoryMethod pattern - Creating a GUI editor for the Contact

The GUI defined in the EditorGui class is a truly generic editor.
It accepts an argument of type ItemEditor, and delegates
 all editing tasks to its ItemEditorInterface and the associated GUI.
 The getEditor() Factory Method is used to obtain the ItemEditor
 for the example.

Notice that the editor in the top portion of the GUI is,
 in fact, returned by the ItemEditor belonging to the
 ContactEditableImpl class, and has appropriate fields for that class.

Creating a Contact object.
Figure-2.13: Result of running FactoryMethodPattern application

Solution: This exercise up to this point is provided as a ready-to-open-and-run NetBeans project as part of hands-on lab zip file. You can find it as <LAB_UNZIPPED_DIRECTORY>/javapatterns/samples/FactoryMethodPatternYou can just open it and run it. 

                                                                                                              return to top of the exercise



Summary

In this exercise,  you have learned about FactoryMethod pattern.

                                                                                                                        return to the top



Exercise 3: AbstractFactory Pattern


In this exercise, you are going to learn how to write a Java program that uses Abstract factory pattern.


(3.1) Abstract factory pattern example


1. Create a new NetBeans project
2. Modify the IDE generated AbstractFactoryPattern.java as shown in Code-3.11 below.  Study the code by paying special attention to the bold fonted parts.

public class AbstractFactoryPattern {
   
    public static void main(String [] arguments){
       
        System.out.println("Example for the AbstractFactory pattern");
        System.out.println();
        System.out.println("  Notice that you can");
        System.out.println("  use the Address and PhoneNumber classes when writing");
        System.out.println("  almost all of the code. This allows you to write a very");
        System.out.println("  generic framework, and plug in Concrete Factories");
        System.out.println("  and Products to specialize the behavior of your code)");
        System.out.println();
       
        System.out.println("Creating U.S. Address and Phone Number:");
        AddressFactoryInterface addressFactory = new USAddressFactoryImpl();
        AddressAbstractClass address = addressFactory.createAddress();
        PhoneNumberAbstractClass phone = addressFactory.createPhoneNumber();
       
        address.setStreet("142 Lois Lane");
        address.setCity("Metropolis");
        address.setRegion("WY");
        address.setPostalCode("54321");
        phone.setPhoneNumber("7039214722");
       
        System.out.println("U.S. address:");
        System.out.println(address.getFullAddress());
        System.out.println("U.S. phone number:");
        System.out.println(phone.getPhoneNumber());
        System.out.println();
        System.out.println();
       
        System.out.println("Creating French Address and Phone Number:");
        addressFactory = new FrenchAddressFactoryImpl();
        address = addressFactory.createAddress();
        phone = addressFactory.createPhoneNumber();
       
        address.setStreet("21 Rue Victor Hugo");
        address.setCity("Courbevoie");
        address.setPostalCode("40792");
        phone.setPhoneNumber("011324290");
       
        System.out.println("French address:");
        System.out.println(address.getFullAddress());
        System.out.println("French phone number:");
        System.out.println(phone.getPhoneNumber());
    }
}
Code-3.11: AbstractFactoryPattern.java

3. Write AddressFactoryInterface.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

interface AddressFactoryInterface {
    public AddressAbstractClass createAddress();
    public PhoneNumberAbstractClass createPhoneNumber();
}
Code-3.12: AddressFactoryInterface.java

4. Write USAddressFactoryImpl.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class USAddressFactoryImpl implements AddressFactoryInterface{
   
    public AddressAbstractClass createAddress(){
        return new USAddress();
    }
   
    public PhoneNumberAbstractClass createPhoneNumber(){
        return new USPhoneNumber();
    }
}
Code-3.12: USAddressFactoryImpl.java

5. Write FrenchAddressFactoryImpl.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class FrenchAddressFactoryImpl implements AddressFactoryInterface {
   
    public AddressAbstractClass createAddress(){
        return new FrenchAddress();
    }
   
    public PhoneNumberAbstractClass createPhoneNumber(){
        return new FrenchPhoneNumber();
    }
}
Code-3.12: FrenchAddressFactoryImpl.java

6. Write AddressAbstractClass.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

abstract class AddressAbstractClass{
   
    private String street;
    private String city;
    private String region;
    private String postalCode;
   
    public static final String EOL_STRING =
            System.getProperty("line.separator");
    public static final String SPACE = " ";
   
    public String getStreet(){ return street; }
    public String getCity(){ return city; }
    public String getPostalCode(){ return postalCode; }
    public String getRegion(){ return region; }
   
    // Abstract method
    public abstract String getCountry();
   
    public String getFullAddress(){
        return street + EOL_STRING +
                city + SPACE + postalCode + EOL_STRING;
    }
   
    public void setStreet(String newStreet){ street = newStreet; }
    public void setCity(String newCity){ city = newCity; }
    public void setRegion(String newRegion){ region = newRegion; }
    public void setPostalCode(String newPostalCode){ postalCode = newPostalCode; }
}
Code-3.12: AddressAbstractClass.java

7. Write PhoneNumberAbstractClass.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

abstract class PhoneNumberAbstractClass{
   
    private String phoneNumber;
   
    // Abstract method
    public abstract String getCountryCode();
   
    public String getPhoneNumber(){ return phoneNumber; }
   
    public void setPhoneNumber(String newNumber){
        try{
            Long.parseLong(newNumber);
            phoneNumber = newNumber;
        } catch (NumberFormatException exc){
        }
    }
}
Code-3.12: AddressFactoryInterface.java

8. Write USAddress.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class USAddress extends AddressAbstractClass{
   
    private static final String COUNTRY = "UNITED STATES";
    private static final String COMMA = ",";
   
    // Concrete method
    public String getCountry(){ return COUNTRY; }
   
    public String getFullAddress(){
        return getStreet() + EOL_STRING +
                getCity() + COMMA + SPACE + getRegion() +
                SPACE + getPostalCode() + EOL_STRING +
                COUNTRY + EOL_STRING;
    }
}
Code-3.12: USAddress.java

9. Write FrenchAddress.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class FrenchAddress extends AddressAbstractClass {
   
    private static final String COUNTRY = "FRANCE";
   
    // Concrete method
    public String getCountry(){ return COUNTRY; }
   
    public String getFullAddress(){
        return getStreet() + EOL_STRING +
            getPostalCode() + SPACE + getCity() +
            EOL_STRING + COUNTRY + EOL_STRING;
    }
}
Code-3.12: FrenchAddress.java

10. Write USPhoneNumber.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class USPhoneNumber extends PhoneNumberAbstractClass{
   
    private static final String COUNTRY_CODE = "01";
    private static final int NUMBER_LENGTH = 10;
   
    // Concrete method
    public String getCountryCode(){ return COUNTRY_CODE; }
   
    public void setPhoneNumber(String newNumber){
        if (newNumber.length() == NUMBER_LENGTH){
            super.setPhoneNumber(newNumber);
        }
    }
}
Code-3.12: USPhoneNumber.java

11. Write FrenchPhoneNumber.java as shown in Code-3.12 below.  Study the code by paying special attention to the bold fonted parts.

class FrenchPhoneNumber extends PhoneNumberAbstractClass{
   
    private static final String COUNTRY_CODE = "33";
    private static final int NUMBER_LENGTH = 9;
   
    // Concrete method
    public String getCountryCode(){ return COUNTRY_CODE; }
   
    public void setPhoneNumber(String newNumber){
        if (newNumber.length() == NUMBER_LENGTH){
            super.setPhoneNumber(newNumber);
        }
    }
}
Code-3.12: FrenchPhoneNumber.java

12. Build and run the project
Example for the AbstractFactory pattern

  Notice that you can
  use the Address and PhoneNumber classes when writing
  almost all of the code. This allows you to write a very
  generic framework, and plug in Concrete Factories
  and Products to specialize the behavior of your code)

Creating U.S. Address and Phone Number:
U.S. address:
142 Lois Lane
Metropolis, WY 54321
UNITED STATES

U.S. phone number:
7039214722


Creating French Address and Phone Number:
French address:
21 Rue Victor Hugo
40792 Courbevoie
FRANCE

French phone number:
011324290
Figure-3.13: Result of running AbstractFactoryPattern application

Solution: This exercise up to this point is provided as a ready-to-open-and-run NetBeans project as part of hands-on lab zip file. You can find it as <LAB_UNZIPPED_DIRECTORY>/javapatterns/samples/AbstractFactoryPatternYou can just open it and run it.

                                                                                                              return to top of the exercise

<>

Summary


In this exercise, you have learned AbstractFactory pattern.


                                                                                                                    return to the top






Homework exercise (for people who are taking Sang Shin's "Java Programming online course")


1. <to be provided>

2. Send the following files to javaintro1homework@sun.com with Subject as JavaIntro-javapatterns.