Practical Analysis of the Observer Pattern
Generally, there are three mechanisms for event handling in object-oriented analysis and design. Besides ordinary function calls, callback functions are often used. Additionally, J2EE provides an event handling mechanism based on listeners. Please research the mechanisms of Action and ActionListener, and complete an analysis example.
First, understand the Observer Pattern.
Observer Pattern
Observers in the Real World
The Observer Pattern is also known as the Publish-Subscribe Pattern. For example, in RSS subscriptions (the predecessor of WeChat push notifications), when a blogger (the subject) updates a post, the subscribers’ readers (observers) automatically receive the update. This publish-subscribe approach is what we call the Observer Pattern.
The Observer Pattern
The Gang of Four (GoF) describes the Observer Pattern as follows:
Intent
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Also Known As
Dependents, Publish-Subscribe
Motivation
A common side effect of partitioning a system into a collection of cooperating classes is the need to maintain consistency between related objects. You don’t want to achieve consistency by making the classes tightly coupled, because that reduces their reusability.
…
Participants
Subject
Knows its observers. Any number of Observer objects may observe a subject.
Provides an interface for attaching and detaching Observer objects.
Observer
Defines an updating interface for objects that should be notified of changes in a subject.
ConcreteSubject
Stores state of interest to ConcreteObserver objects.
Sends a notification to its observers when its state changes.
ConcreteObserver
Maintains a reference to a ConcreteSubject object.
Stores state that should stay consistent with the subject’s.
Implements the Observer updating interface to keep its state consistent with the subject’s.
It can be observed that the Observer Pattern is often applied in scenarios where:
- Two objects have a dependency relationship.
- When one object changes, other objects need to be updated simultaneously.
Compared to ordinary function calls and callback functions, using the Observer Pattern allows for low-cost maintenance of consistency between objects, significantly reducing the degree of coupling between objects. At the same time, it achieves a broadcasting effect, which the former two cannot. The use of the Observer Pattern greatly improves code reusability.
Action and ActionListener in J2EE are a vivid example of the Observer Pattern.
Action & ActionListener in J2EE
Action & ActionListener can be well applied in the MVC paradigm to reduce the coupling between the model and the view.
A good example is found, with the code as follows:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
public class Frame extends JFrame {
ActionListener observer1 = new Observer("First Observer");
ActionListener observer2 = new Observer("Second Observer");
ActionListener observer3 = new Observer("Third Observer");
public Frame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
}
private void init(){
addChangingTextField();
pack();
setVisible(true);
}
private void addChangingTextField(){
//Observable class TextField. each time
//An action or event occurs the observers will be updated
//Almost all the Swing classes has this kind of behavior
JTextField dataTextField = new JTextField();
// Observer registering to the observable class.
dataTextField.addActionListener(observer1);
dataTextField.addActionListener(observer2);
dataTextField.addActionListener(observer3);
add(dataTextField);
}
public static void main(String [] args){
Frame frame = new Frame();
}
}
class Observer implements ActionListener{
private String name;
private String message;
public Observer (String s){
name = s;
}
public void actionPerformed(ActionEvent e) {
JTextField textField = (JTextField) e.getSource();
message = textField.getText();
JOptionPane.showMessageDialog(null,
name + " has been confirmed about the change, Thanks!! "
+ " The Message was: " + message);
}
}
Analysis
The two classes play the roles of Action/ActionListener respectively. frame.dataTextField binds three listeners (observer1, observer2, observer3) in the addChangingTextField method. After the text field in the frame is edited (state change), the actionPerformed method in Observer is triggered, displaying the corresponding text.
In this example, frame.dataTextField acts as the ConcreteSubject, while observer1, observer2, and observer3 act as ConcreteObserver. The interfaces they implement, which define the Action/ActionListener methods, correspond to Subject and Observer respectively.
After binding observers, the subject does not concern itself with the specific behavior of the observers. It only notifies the observers when its own state changes, leaving it up to the observers to decide what action to take. Therefore, this approach is quite elegant—far superior to explicitly calling functions or executing callback functions to trigger actions. It’s definitely worth learning.