Behavioral Patterns  «Prev  Next»
Lesson 7 Observer: variations
ObjectiveWrite a Time Class as an Observed Object.

Write Time Class as an Observed Object

The TimeObserver interface serves as a contract for classes that wish to be notified when time changes. Implementing this interface ensures that the class has the `timeChanged` method, which gets invoked with the new time value as an argument. Expand the following TimeObserver interface into a Java Program.
public interface TimeObserver {

/**
 * Called to notify the implementing class that the time has changed. 
 * @param newTime The new time value, represented as an integer.
 */
void timeChanged(int newTime);
}

Java Program that includes the TimeObserver interface

Here’s an expanded Java program that includes the `TimeObserver` interface, a Clock class (the subject), and a concrete implementation of the `TimeObserver` interface. The program demonstrates how the Observer Pattern works in practice.
// TimeObserver.java
public interface TimeObserver {

    /**
     * Called to notify the implementing class that the time has changed. 
     * @param newTime The new time value, represented as an integer.
     */
    void timeChanged(int newTime);
}

// Clock.java (Subject)
import java.util.ArrayList;
import java.util.List;

public class Clock {
    private List<TimeObserver> observers = new ArrayList<>();
    private int currentTime;

    /**
     * Adds an observer to the list of observers.
     * @param observer The observer to be added.
     */
    public void addObserver(TimeObserver observer) {
        observers.add(observer);
    }

    /**
     * Removes an observer from the list of observers.
     * @param observer The observer to be removed.
     */
    public void removeObserver(TimeObserver observer) {
        observers.remove(observer);
    }

    /**
     * Updates the current time and notifies all observers of the change.
     * @param newTime The new time value.
     */
    public void setTime(int newTime) {
        this.currentTime = newTime;
        notifyObservers();
    }

    /**
     * Notifies all observers that the time has changed.
     */
    private void notifyObservers() {
        for (TimeObserver observer : observers) {
            observer.timeChanged(currentTime);
        }
    }
}

// DigitalClock.java (Concrete Observer)
public class DigitalClock implements TimeObserver {
    private String clockName;

    public DigitalClock(String clockName) {
        this.clockName = clockName;
    }

    @Override
    public void timeChanged(int newTime) {
        System.out.println(clockName + " displays updated time: " + newTime);
    }
}

// Main.java (Demo Program)
public class Main {
    public static void main(String[] args) {
        // Create a Clock (Subject)
        Clock clock = new Clock();

        // Create two Digital Clocks (Observers)
        DigitalClock clock1 = new DigitalClock("Clock 1");
        DigitalClock clock2 = new DigitalClock("Clock 2");

        // Register observers with the clock
        clock.addObserver(clock1);
        clock.addObserver(clock2);

        // Simulate time changes
        System.out.println("Time update 1:");
        clock.setTime(1200); // Notify observers of new time 1200

        System.out.println("\nTime update 2:");
        clock.setTime(1230); // Notify observers of new time 1230

        // Remove one observer and update time again
        System.out.println("\nRemoving Clock 1 and updating time:");
        clock.removeObserver(clock1);
        clock.setTime(1300); // Notify remaining observers of new time 1300
    }
}

Explanation:
  1. TimeObserver Interface:
    • Defines the timeChanged method that all observers must implement.
  2. Clock Class:
    • Acts as the subject, maintaining a list of observers.
    • Notifies all registered observers when the time changes via the notifyObservers method.
  3. DigitalClock Class:
    • Implements the TimeObserver interface and provides its own logic for handling time updates.
  4. Main Class:
    • Demonstrates registering observers, updating time, and notifying observers of changes.

Sample Output:
Time update 1:
Clock 1 displays updated time: 1200
Clock 2 displays updated time: 1200

Time update 2:
Clock 1 displays updated time: 1230
Clock 2 displays updated time: 1230

Removing Clock 1 and updating time:
Clock 2 displays updated time: 1300
This program effectively showcases the Observer Pattern in action with the `TimeObserver` interface. Let me know if you need further modifications or enhancements!

Variations on the Observer Pattern

There are many variations on the Observer pattern. Three of the more common variations are:
  1. To make the associated classes concrete. It is often quite easy to make the Observed class concrete, especially if you only plan on having one Observer. The main advantage of making the Observed class abstract is that different classes can share the same list management logic.
  2. To require the Observed object to pass the changed state to the Observer object's update() method.
  3. To manipulate exactly which state changes trigger notifications. It may be the case that not all changes of state are of equal interest to Observers. For instance, an object observing a word processing document may care a great deal if the user types a new letter, but not much at all if they merely scroll the screen up or down.

So far, I have implicitly assumed a multicast model for the Observed object; that is, a change to a single Observed object may result in notifications to an indefinite number of objects. Occasionally, you may want to use a unicast model that only allows a single Observer object to be registered with one Observed object at a time.

A typical observer is an object with interest or dependency in the state of the subject. A subject can have more than one such observer and each of these observers needs to know when the subject undergoes a change in its state. The subject cannot maintain a static list of such observers as the list of observers for a given subject could change dynamically. Hence, any object with interest in the state of the subject needs to explicitly register itself as an observer with the subject. Whenever the subject undergoes a change in its state, it notifies all of its registered observers. Upon receiving notification from the subject, each of the observers queries the subject to synchronize its state with that of the subject's. Thus a subject behaves as a publisher by publishing messages to all of its subscribing observers. In other words, the scenario contains a one-to-many relationship between a subject and the set of its observers. Whenever the subject instance undergoes a state change, all of its dependent observers are notified and they can update themselves. Each of the observer objects has to register itself with the subject to get notified when there is a change in the subject's state. An observer can register or subscribe with multiple subjects. Whenever an observer does not wish to be notified any further, it unregisters itself with the subject. For this mechanism to work:
  • The subject should provide an interface for registering and unregistering for change notifications
  • One of the following two must be true:
    1. In the pull model: The subject should provide an interface that enables observers to query the subject for the required state information to update their state.
    2. In the push model: The subject should send the state information that the observers may be interested in.
  • Observers should provide an interface for receiving notifications from the subject.

Time Observer Interface - Exercise

In this exercise, you will write a Time class using the Observer pattern.
Time Observer Interface - Exercise

SEMrush Software 7 SEMrush Banner 7