SE450: Horstmann Chapter 10

Contents [0/55]

Object-Oriented Design & Patterns [1/55]
Chapter Topics [2/55]
Adapters [3/55]
The ADAPTER Pattern [4/55]
The ADAPTER Pattern [5/55]
The ADAPTER Pattern [6/55]
The ADAPTER Pattern [7/55]
The ADAPTER Pattern [8/55]
The ADAPTER Pattern [9/55]
User Interface Actions [10/55]
User Interface Actions [11/55]
The Action Interface Type [12/55]
The Action Interface Type [13/55]
Action Example [14/55]
The COMMAND Pattern [15/55]
The COMMAND Pattern [16/55]
The COMMAND Pattern [17/55]
The COMMAND Pattern [18/55]
Factory Methods [19/55]
The FACTORY METHOD Pattern [20/55]
The FACTORY METHOD Pattern [21/55]
The FACTORY METHOD Pattern [22/55]
The FACTORY METHOD Pattern [23/55]
Not a FACTORY METHOD [24/55]
Proxies [25/55]
Deferred Image Loading [26/55]
Deferred Image Loading [27/55]
Proxies [28/55]
The PROXY Pattern [29/55]
The PROXY Pattern [30/55]
The PROXY Pattern [31/55]
The PROXY Pattern [32/55]
Singletons [33/55]
Random Number Generator Singleton [34/55]
The SINGLETON Pattern [35/55]
The SINGLETON Pattern [36/55]
Not a SINGLETON [37/55]
Inflexible Hierarchies [38/55]
Inflexible Hierarchies [39/55]
Visitors [40/55]
Visitors [41/55]
Visitors [42/55]
Double Dispatch [43/55]
Visitor Example [44/55]
Visitor Example [45/55]
Visitor Example [46/55]
Visitor Example [47/55]
Double Dispatch Example [48/55]
Double Dispatch Example [49/55]
The VISITOR Pattern [50/55]
The VISITOR Pattern [51/55]
The VISITOR Pattern [52/55]
The VISITOR Pattern [53/55]
Other Design Patterns [54/55]
Conclusion: What You Learned [55/55]

Object-Oriented Design & Patterns [1/55]

Cay S. Horstmann

Chapter 10

More Design Patterns

horstmann-oodp2

Chapter Topics [2/55]

Adapters [3/55]

file:horstmann/ch10_adapter/IconAdapter.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package horstmann.ch10_adapter;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.JComponent;

/**
   An adapter that turns an icon into a JComponent.
 */
@SuppressWarnings("serial")
public class IconAdapter extends JComponent
{
  /**
      Constructs a JComponent that displays a given icon.
      @param icon the icon to display
   */
  public IconAdapter(Icon icon)
  {
    this.icon = icon;
  }

  public void paintComponent(Graphics g)
  {
    icon.paintIcon(this, g, 0, 0);
  }

  public Dimension getPreferredSize()
  {
    return new Dimension(icon.getIconWidth(),
        icon.getIconHeight());
  }

  private Icon icon;
}

file:horstmann/ch10_adapter/IconAdapterTester.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package horstmann.ch10_adapter;
import java.awt.BorderLayout;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;

/**
   This program demonstrates how an icon is adapted to
   a component. The component is added to a frame.
 */
public class IconAdapterTester
{
  public static void main(String[] args)
  {
    Icon icon = new CarIcon(300);
    JComponent component = new IconAdapter(icon);

    JFrame frame = new JFrame();
    frame.add(component, BorderLayout.CENTER);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}

The ADAPTER Pattern [4/55]

Context

  1. You want to use an existing class (adaptee) without modifying it.
  2. The context in which you want to use the class requires target interface that is different from that of the adaptee.
  3. The target interface and the adaptee interface are conceptually related.

The ADAPTER Pattern [5/55]

Solution

  1. Define an adapter class that implements the target interface.
  2. The adapter class holds a reference to the adaptee. It translates target methods to adaptee methods.
  3. The client wraps the adaptee into an adapter class object.

The ADAPTER Pattern [6/55]

.

The ADAPTER Pattern [7/55]

Name in Design Pattern
Actual Name (Icon->Component)
Adaptee
Icon
Target
JComponent
Adapter
IconAdapter
Client
The class that wants to add icons into a container
targetMethod()
paintComponent(), getPreferredSize()
adapteeMethod()
paintIcon(), getIconWidth(), getIconHeight()

The ADAPTER Pattern [8/55]

The ADAPTER Pattern [9/55]

Name in Design Pattern
Actual Name (Stream->Reader)
Adaptee
InputStream
Target
Reader
Adapter
InputStreamReader
Client
The class that wants to read text from an input stream
targetMethod()
read (reading a character)
adapteeMethod()
read (reading a byte)

User Interface Actions [10/55]

User Interface Actions [11/55]


The Action Interface Type [12/55]

The Action Interface Type [13/55]

.

Action Example [14/55]

file:horstmann/ch10_command/CommandTester.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package horstmann.ch10_command;
import java.awt.BorderLayout;

import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JTextArea;
import javax.swing.JToolBar;

/**
   This program demonstrates action objects. Two actions
   insert greetings into a text area. Each action can be
   triggered by a menu item or toolbar button. When an
   action is carried out, the opposite action becomes enabled.
 */
public class CommandTester
{
  public static void main(String[] args)
  {
    JFrame frame = new JFrame();
    JMenuBar bar = new JMenuBar();
    frame.setJMenuBar(bar);
    JMenu menu = new JMenu("Say");
    bar.add(menu);
    JToolBar toolBar = new JToolBar();
    frame.add(toolBar, BorderLayout.NORTH);
    JTextArea textArea = new JTextArea(10, 40);
    frame.add(textArea, BorderLayout.CENTER);

    GreetingAction helloAction = new GreetingAction(
        "Hello, World", textArea);
    helloAction.putValue(Action.NAME, "Hello");
    helloAction.putValue(Action.SMALL_ICON,
        new ImageIcon("hello.png"));

    GreetingAction goodbyeAction = new GreetingAction(
        "Goodbye, World", textArea);
    goodbyeAction.putValue(Action.NAME, "Goodbye");
    goodbyeAction.putValue(Action.SMALL_ICON,
        new ImageIcon("goodbye.png"));

    helloAction.setOpposite(goodbyeAction);
    goodbyeAction.setOpposite(helloAction);
    goodbyeAction.setEnabled(false);

    menu.add(helloAction);
    menu.add(goodbyeAction);

    toolBar.add(helloAction);
    toolBar.add(goodbyeAction);

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}

file:horstmann/ch10_command/GreetingAction.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package horstmann.ch10_command;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JTextArea;

/**
   This action places a greeting into a text field
   and afterwards disables itself and enables its
   opposite action.
 */
@SuppressWarnings("serial")
public class GreetingAction extends AbstractAction
{
  /**
      Constructs a greeting action.
      @param greeting the string to add to the text area
      @param textArea the text area to which to add the greeting
   */
  public GreetingAction(String greeting, JTextArea textArea)
  {
    this.greeting = greeting;
    this.textArea = textArea;
  }

  /**
      Sets the opposite action.
      @param action the action to be enabled after this action was
      carried out
   */
  public void setOpposite(Action action)
  {
    oppositeAction = action;
  }

  public void actionPerformed(ActionEvent event)
  {
    textArea.append(greeting);
    textArea.append("\n");
    if (oppositeAction != null)
    {
      setEnabled(false);
      oppositeAction.setEnabled(true);
    }
  }

  private String greeting;
  private JTextArea textArea;
  private Action oppositeAction;
}

The COMMAND Pattern [15/55]

Context

  1. You want to implement commands that behave like objects

The COMMAND Pattern [16/55]

Solution

  1. Define a command interface type with a method to execute the command.
  2. Supply methods in the command interface type to manipulate the state of command objects.
  3. Each concrete command class implements the command interface type.
  4. To invoke the command, call the execute method.

The COMMAND Pattern [17/55]

.

The COMMAND Pattern [18/55]

Name in Design Pattern
Actual Name (Swing actions)
Command
Action
ConcreteCommand
subclass of AbstractAction
execute()
actionPerformed()
state
name and icon

Factory Methods [19/55]

The FACTORY METHOD Pattern [20/55]

Context

  1. A type (the creator) creates objects of another type (the product).
  2. Subclasses of the creator type need to create different kinds of product objects.
  3. Clients do not need to know the exact type of product objects.

The FACTORY METHOD Pattern [21/55]

Solution

  1. Define a creator type that expresses the commonality of all creators.
  2. Define a product type that expresses the commonality of all products.
  3. Define a method, called the factory method, in the creator type.
    The factory method yields a product object.
  4. Each concrete creator class implements the factory method so that it returns an object of a concrete product class.

The FACTORY METHOD Pattern [22/55]

.

The FACTORY METHOD Pattern [23/55]

Name in Design Pattern
Actual Name (iterator)
Creator
Collection
ConcreteCreator
A subclass of Collection
factoryMethod()
iterator()
Product
Iterator
ConcreteProduct
A subclass of Iterator (which is often anonymous)

Not a FACTORY METHOD [24/55]

Proxies [25/55]

Deferred Image Loading [26/55]



Deferred Image Loading [27/55]

Proxies [28/55]

file:horstmann/ch10_proxy/ImageProxy.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package horstmann.ch10_proxy;
import java.awt.Component;
import java.awt.Graphics;

import javax.swing.Icon;
import javax.swing.ImageIcon;

/**
   A proxy for delayed loading of image icons.
 */
public class ImageProxy implements Icon
{
  /**
      Constructs a proxy for delayed loading of an image file.
      @param name the file name
   */
  public ImageProxy(String name)
  {
    this.name = name;
    image = null;
  }

  public void paintIcon(Component c, Graphics g, int x, int y)
  {
    ensureImageLoaded();
    image.paintIcon(c, g, x, y);
  }

  public int getIconWidth()
  {
    ensureImageLoaded();
    return image.getIconWidth();
  }

  public int getIconHeight()
  {
    ensureImageLoaded();
    return image.getIconHeight();
  }

  /**
      Loads the image if it hasn't been loaded yet. Prints
      a message when the image is loaded.
   */
  private void ensureImageLoaded()
  {
    if (image == null)
    {
      System.out.println("Loading " + name);
      image = new ImageIcon(name);
    }
  }

  private String name;
  private ImageIcon image;
}

file:horstmann/ch10_proxy/ProxyTester.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package horstmann.ch10_proxy;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTabbedPane;

/**
   This program demonstrates the use of the image proxy.
   Images are only loaded when you press on a tab.
 */
public class ProxyTester
{
  public static void main(String[] args)
  {
    JTabbedPane tabbedPane = new JTabbedPane();
    for (String name : imageNames)
    {
      JLabel label = new JLabel(new ImageProxy(name));
      tabbedPane.add(name, label);
    }

    JFrame frame = new JFrame();
    frame.add(tabbedPane);

    frame.setSize(FRAME_WIDTH, FRAME_HEIGHT);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
  }

  private static final String[] imageNames =
    {
        "devonian.gif",
        "permian.gif",
        "jurassic1.gif",
        "jurassic2.gif",
        "cretaceous1.gif",
        "cretaceous2.gif",
        "cretaceous3.gif",
        "eocene1.gif",
        "eocene2.gif",
        "oligocene.gif",
        "miocene.gif",
        "pleistocene.gif"
    };

  private static final int FRAME_WIDTH = 500;
  private static final int FRAME_HEIGHT = 300;
}

The PROXY Pattern [29/55]

Context

  1. A class (the real subject) provides a service that is specified by an interface type (the subject type)
  2. There is a need to modify the service in order to make it more versatile.
  3. Neither the client nor the real subject should be affected by the modification.

The PROXY Pattern [30/55]

Solution

  1. Define a proxy class that implements the subject interface type.
    The proxy holds a reference to the real subject, or otherwise knows how to locate it.
  2. The client uses a proxy object.
  3. Each proxy method invokes the same method on the real subject and provides the necessary modifications.

The PROXY Pattern [31/55]

.

The PROXY Pattern [32/55]

Name in Design Pattern
Actual Name (image proxy)
Subject
Icon
RealSubject
ImageIcon
Proxy
ImageProxy
request()
The methods of the Icon interface type
Client
JLabel

Singletons [33/55]

Random Number Generator Singleton [34/55]

public class SingleRandom
{
   private SingleRandom() { generator = new Random(); }
   public void setSeed(int seed) { generator.setSeed(seed); }
   public int nextInt() { return generator.nextInt(); }
   public static SingleRandom getInstance() { return instance; }
   private Random generator;
   private static SingleRandom instance = new SingleRandom();
}

The SINGLETON Pattern [35/55]

Context

  1. All clients need to access a single shared instance of a class.
  2. You want to ensure that no additional instances can be created accidentally.

The SINGLETON Pattern [36/55]

Solution

  1. Define a class with a private constructor.
  2. The class constructs a single instance of itself.
  3. Supply a static method that returns a reference to the single instance.

Not a SINGLETON [37/55]

Inflexible Hierarchies [38/55]

Inflexible Hierarchies [39/55]

.

Visitors [40/55]

Visitors [41/55]

Visitors [42/55]

Double Dispatch [43/55]

Visitor Example [44/55]

Visitor Example [45/55]

.

Visitor Example [46/55]

Visitor Example [47/55]

file:horstmann/ch10_visitor/FileSystemNode.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
package horstmann.ch10_visitor;
/**
   The common interface for file and directory nodes.
 */
public interface FileSystemNode
{
  void accept(FileSystemVisitor v);
}

file:horstmann/ch10_visitor/FileNode.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
package horstmann.ch10_visitor;
import java.io.File;

public class FileNode implements FileSystemNode
{
  public FileNode(File file)
  {
    this.file = file;
  }

  public File getFile() { return file; }

  public void accept(FileSystemVisitor v)
  {
    v.visitFileNode(this);
  }

  private File file;
}

file:horstmann/ch10_visitor/DirectoryNode.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package horstmann.ch10_visitor;
import java.io.File;

public class DirectoryNode implements FileSystemNode
{
  public DirectoryNode(File directory)
  {
    this.directory = directory;
  }

  public void accept(FileSystemVisitor v)
  {
    v.visitDirectoryNode(this);
  }

  public File getDirectory() { return directory; }

  public FileSystemNode[] getChildren()
  {
    File[] files = directory.listFiles();
    FileSystemNode[] children = new FileSystemNode[files.length];
    for (int i = 0; i < files.length; i++)
    {
      File f = files[i];
      if (f.isDirectory())
        children[i] = new DirectoryNode(f);
      else
        children[i] = new FileNode(f);
    }
    return children;
  }

  private File directory;
}

file:horstmann/ch10_visitor/FileSystemVisitor.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
package horstmann.ch10_visitor;
/**
   The visitor interface type for visiting file system nodes.
 */
public interface FileSystemVisitor
{
  /**
      Visits a file node.
      @param node the file node
   */
  void visitFileNode(FileNode node);

  /**
      Visits a directory node.
      @param node the directory node
   */
  void visitDirectoryNode(DirectoryNode node);
}

file:horstmann/ch10_visitor/PrintVisitor.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
package horstmann.ch10_visitor;

public class PrintVisitor implements FileSystemVisitor
{
  public void visitFileNode(FileNode node)
  {
    for (int i = 0; i < level; i++) System.out.print(" ");
    System.out.println(node.getFile().getName());
  }

  public void visitDirectoryNode(DirectoryNode node)
  {
    for (int i = 0; i < level; i++) System.out.print(" ");
    System.out.println(node.getDirectory().getName());
    level++;
    for (FileSystemNode c : node.getChildren())
      c.accept(this);
    level--;
  }

  private int level = 0;
}

file:horstmann/ch10_visitor/VisitorTester.java [source] [doc-public] [doc-private]
01
02
03
04
05
06
07
08
09
10
11
package horstmann.ch10_visitor;
import java.io.File;

public class VisitorTester
{
  public static void main(String[] args)
  {
    DirectoryNode node = new DirectoryNode(new File(".."));
    node.accept(new PrintVisitor());
  }
}

Double Dispatch Example [48/55]

Double Dispatch Example [49/55]

.

The VISITOR Pattern [50/55]

Context

  1. An object structure contains element classes of multiple types, and you want to carry out operations that depend on the object types.
  2. The set of operations should be extensible over time.
  3. The set of element classes is fixed.

The VISITOR Pattern [51/55]

Solution

  1. Define a visitor interface type that has methods for visiting elements of each of the given types.
  2. Each element class defines an accept method that invokes the matching element visitation method on the visitor parameter.
  3. To implement an operation, define a class that implements the visitor interface type and supplies the operation s action for each element type.

The VISITOR Pattern [52/55]

.

The VISITOR Pattern [53/55]

Name in Design Pattern
Actual Name (file system visitor)
Element
FileSystemNode
ConcreteElement
FileNode, DirectoryNode
Visitor
FileSystemVisitor
ConcreteVisitor
PrintVisitor

Other Design Patterns [54/55]

Conclusion: What You Learned [55/55]

  1. Object-oriented design  
    The design methodology  
    CRC cards and UML diagrams  
    Design patterns
  2. Advanced Java  
    Interface types, polymorphism, and inheritance  
    Inner classes  
    Reflection  
    Multithreading  
    Collections
  3. User interface programming  
    Building Swing applications  
    Event handling  
    Graphics programming

Revised: 2007/09/11 16:42