Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Applied Java™ Patterns - Stephen Stelting, Olav Maassen.pdf
Скачиваний:
202
Добавлен:
24.05.2014
Размер:
2.84 Mб
Скачать

ConcreteObserver – Implements the Observable interface and determines in each implemented method how to respond to the message received from the Observable.

Normally the application framework registers the specific observers to the Observable.

Benefits and Drawbacks

The Observer pattern’s flexibility carries with it the added benefit that the observable object can be relatively simple. There is not a substantial amount of coding overhead. In addition, the pattern is useful for:

Testing – You can code an echo observer that can display the observable’s behavior.

Incremental development – It’s very easy to add additional observers as you code them.

The principal challenge for this pattern comes from implementation of the messaging model: you can use a specific or generic message broadcast strategy. Each approach has potential disadvantages.

Generic messaging – As messaging becomes more generic, it can become more difficult to determine what is going on for an observable component. Generic messaging can result in unnecessary message traffic; some events could be broadcast to observers that would otherwise not care about them. Generic messaging also can result in additional coding overhead for observers, because they have to decode messages.

Specific messaging – More-specific messages place greater coding requirements on the observable component, since they must produce a series of notifications under specific conditions. They might also make observers more complex, because observers must handle a variety of message types.

Pattern Variants

The Observer pattern has several variants that can be used in defining the relationship between observable and observer:

Single observer versus multiple observers – Depending on the role of the observable component, it might support only a single observer.

Multithreaded observable components – If an observable object is multithreaded, it can provide support for a message queue, and can provide services such as message priority and override behavior.

Client pull – Although the pattern is oriented toward server push, you can modify it to support a limited form of client pull. In this variant, the observable typically provides the observers with notification that an event has taken place. If observers require more detail, they contact the observable, calling a method that requests additional information about the event.

Related Patterns

Related patterns include Proxy (page 197). For distributed communication, the Remote Proxy pattern is often used to manage communication between the Observer and Observable.

Example

Note:

For a full working example of this code example, with additional supporting classes and/or a RunPattern class, see “ Observer ” on page 408 of the “ Full Code Examples ” appendix.

In the Observer example, an observer sends updates about the state of a Task to all registered listeners in a GUI.

It's important to recognize that any Java GUI code normally uses the Observer pattern for event handling. When you write a class that implements a listener interface like ActionListener, you are creating an observer. Registering that listener with a component through the method addActionListener associates the observer with an observable element, the Java GUI component.

69

In this example, the observable element is represented by the Task being modified in the GUI. The class

TaskChangeObservable keeps track of the listeners for changes to the Task through the methods

addTaskChangeObserver and removeTaskChangeObserver.

Example 2.32 TaskChangeObservable.java

1.import java.util.ArrayList;

2.import java.util.Iterator;

3.public class TaskChangeObservable{

4.private ArrayList observers = new ArrayList();

6.public void addTaskChangeObserver(TaskChangeObserver observer){

7.if (!observers.contains(observer)){

8. observers.add(observer);

9.}

10.}

11.public void removeTaskChangeObserver(TaskChangeObserver observer){

12.observers.remove(observer);

13.}

14.

15.public void selectTask(Task task){

16.Iterator elements = observers.iterator();

17.while (elements.hasNext()){

18. ((TaskChangeObserver)elements.next()).taskSelected(task);

19.}

20.}

21.public void addTask(Task task){

22.Iterator elements = observers.iterator();

23.while (elements.hasNext()){

24. ((TaskChangeObserver)elements.next()).taskAdded(task);

25.}

26.}

27.public void updateTask(Task task){

28.Iterator elements = observers.iterator();

29.while (elements.hasNext()){

30. ((TaskChangeObserver)elements.next()).taskChanged(task);

31.}

32.}

33.}

TaskChangeObservable has the business methods selectTask, updateTask, and addTask. These methods send

notifications of any changes to a Task.

Every observer must implement the TaskChangeObserver interface, allowing the TaskChangeObservable to call the appropriate method on each observer. If a client were to call the method addTask on the TaskChangeObservable, for instance, the observable object would iterate through its observers and call the taskAdded method on each.

Example 2.33 TaskChangeObserver.java

1.public interface TaskChangeObserver{

2.public void taskAdded(Task task);

3.public void taskChanged(Task task);

4.public void taskSelected(Task task);

5.}

The class ObserverGui provides a GUI in this demonstration, and creates a TaskChangeObservable object. In addition, it creates three panels that implement the TaskChangeObserver interface, and matches them with the TaskChangeObservable object. By doing this, the TaskChangeObservable is able to effectively send updates among the three panels of the GUI.

Example 2.34 ObserverGui.java

1.import java.awt.Container;

2.import java.awt.event.WindowAdapter;

3.import java.awt.event.WindowEvent;

4.import javax.swing.BoxLayout;

5.import javax.swing.JFrame;

6.public class ObserverGui{

7.public void createGui(){

8.JFrame mainFrame = new JFrame("Observer Pattern Example");

9.Container content = mainFrame.getContentPane();

10.content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));

11.TaskChangeObservable observable = new TaskChangeObservable();

70

12.TaskSelectorPanel select = new TaskSelectorPanel(observable);

13.TaskHistoryPanel history = new TaskHistoryPanel();

14.TaskEditorPanel edit = new TaskEditorPanel(observable);

15.observable.addTaskChangeObserver(select);

16.observable.addTaskChangeObserver(history);

17.observable.addTaskChangeObserver(edit);

18.observable.addTask(new Task());

19.content.add(select);

20.content.add(history);

21.content.add(edit);

22.mainFrame.addWindowListener(new WindowCloseManager());

23.mainFrame.pack();

24.mainFrame.setVisible(true);

25.}

26.

27.private class WindowCloseManager extends WindowAdapter{

28.public void windowClosing(WindowEvent evt){

29. System.exit(0);

30.}

31.}

32.}

Example 2.35 TaskEditorPanel.java

1.import java.awt.BorderLayout;

2.import javax.swing.JPanel;

3.import javax.swing.JLabel;

4.import javax.swing.JTextField;

5.import javax.swing.JButton;

6.import java.awt.event.ActionEvent;

7.import java.awt.event.ActionListener;

8.import java.awt.GridLayout;

9.public class TaskEditorPanel extends JPanel implements ActionListener, TaskChangeObserver{

10.private JPanel controlPanel, editPanel;

11.private JButton add, update, exit;

12.private JTextField taskName, taskNotes, taskTime;

13.private TaskChangeObservable notifier;

14.private Task editTask;

15.

public TaskEditorPanel(TaskChangeObservable newNotifier){

16.

17.

notifier = newNotifier;

 

Y

18.

createGui();

 

L

19.

}

 

F

20.

public void createGui(){

A

 

M

21.

setLayout(new BorderLayout());

 

22.

editPanel = new JPanel();

 

 

23.

editPanel.setLayout(new EGridLayout(3, 2));

24.

taskName = new JTextField(20);T

 

25.

taskNotes = new JTextField(20);

26.

taskTime = new JTextField(20);

 

27.

editPanel.add(new JLabel("Task Name"));

28.

editPanel.add(taskName);

 

 

29.

editPanel.add(new JLabel("Task Notes"));

30.

editPanel.add(taskNotes);

 

 

31.

editPanel.add(new JLabel("Time Required"));

32.

editPanel.add(taskTime);

 

 

33.

 

 

 

34.controlPanel = new JPanel();

35.add = new JButton("Add Task");

36.update = new JButton("Update Task");

37.exit = new JButton("Exit");

38.controlPanel.add(add);

39.controlPanel.add(update);

40.controlPanel.add(exit);

41.add.addActionListener(this);

42.update.addActionListener(this);

43.exit.addActionListener(this);

44.add(controlPanel, BorderLayout.SOUTH);

45.add(editPanel, BorderLayout.CENTER);

46.}

47.public void setTaskChangeObservable(TaskChangeObservable newNotifier){

48.notifier = newNotifier;

49.}

50.public void actionPerformed(ActionEvent event){

51.Object source = event.

52.if (source == add){

53. double timeRequired = TEAM0.0; FLY PRESENTS

54. try{

71

55.

timeRequired = Double.parseDouble(taskTime.getText());

56.

}

57.

catch (NumberFormatException exc){}

58.

notifier.addTask(new Task(taskName.getText(), taskNotes.getText(), timeRequired));

59.}

60.else if (source == update){

61.

editTask.setName(taskName.getText());

62.

editTask.setNotes(taskNotes.getText());

63.

try{

64.

editTask.setTimeRequired(Double.parseDouble(taskTime.getText()));

65.

}

66.

catch (NumberFormatException exc){}

67.

notifier.updateTask(editTask);

68.}

69.else if (source == exit){

70.

System.exit(0);

71.

}

72.

 

73.}

74.public void taskAdded(Task task){ }

75.public void taskChanged(Task task){ }

76.public void taskSelected(Task task){

77.editTask = task;

78.taskName.setText(task.getName());

79.taskNotes.setText(task.getNotes());

80.taskTime.setText("" + task.getTimeRequired());

81.}

82.}

Example 2.36 TaskHistoryPanel.java

1.import java.awt.BorderLayout;

2.import javax.swing.JPanel;

3.import javax.swing.JScrollPane;

4.import javax.swing.JTextArea;

5.public class TaskHistoryPanel extends JPanel implements TaskChangeObserver{

6.private JTextArea displayRegion;

7.

8.public TaskHistoryPanel(){

9.createGui();

10.}

11.public void createGui(){

12.setLayout(new BorderLayout());

13.displayRegion = new JTextArea(10, 40);

14.displayRegion.setEditable(false);

15.add(new JScrollPane(displayRegion));

16.}

17.public void taskAdded(Task task){

18.displayRegion.append("Created task " + task + "\n");

19.}

20.public void taskChanged(Task task){

21.displayRegion.append("Updated task " + task + "\n");

22.}

23.public void taskSelected(Task task){

24.displayRegion.append("Selected task " + task + "\n");

25.}

26.}

Example 2.37 TaskSelectorPanel.java

1.import java.awt.event.ActionEvent;

2.import java.awt.event.ActionListener;

3.import javax.swing.JPanel;

4.import javax.swing.JComboBox;

5.public class TaskSelectorPanel extends JPanel implements ActionListener, TaskChangeObserver{

6.private JComboBox selector = new JComboBox();

7.private TaskChangeObservable notifier;

8.public TaskSelectorPanel(TaskChangeObservable newNotifier){

9.notifier = newNotifier;

10.createGui();

11.}

12.public void createGui(){

13.selector = new JComboBox();

14.selector.addActionListener(this);

15.add(selector);

16.}

17.public void actionPerformed(ActionEvent evt){

18.notifier.selectTask((Task)selector.getSelectedItem());

19.}

72

20.public void setTaskChangeObservable(TaskChangeObservable newNotifier){

21.notifier = newNotifier;

22.}

23.

24.public void taskAdded(Task task){

25.selector.addItem(task);

26.}

27.public void taskChanged(Task task){ }

28.public void taskSelected(Task task){ }

29.}

A feature of the Observer pattern is that the Observable uses a standard interface for its Observers— in this case, TaskChangeObserver. This means that the Observer pattern is more generic than the Mediator pattern, but also that the observers may receive some unwanted message traffic. For instance, the TaskEditorPanel takes no action when its taskAdded and taskChanged methods are called.

73