Skip to content

Latest commit

 

History

History
168 lines (130 loc) · 7.35 KB

3G. Observer.md

File metadata and controls

168 lines (130 loc) · 7.35 KB

Observer

The Observer design pattern allows an object (called the subject) to notify other objects (called observers) automatically when its state changes. This allows for loose coupling between the subject and the observers, making it easy to add new observers or remove existing ones without modifying the subject.

image

// Subject Interface
interface Vehicle {
    void attachObserver(VehicleObserver observer);
    void detachObserver(VehicleObserver observer);
    void notifyObservers();
}

// Concrete Subject
class FamilyCar implements Vehicle {
    private String plateNumber;
    private boolean isStolen;
    private List<VehicleObserver> observers = new ArrayList<>();

    public FamilyCar(String plateNumber, boolean isStolen) {
        this.plateNumber = plateNumber;
        this.isStolen = isStolen;
    }

    public String getPlateNumber() {
        return plateNumber;
    }

    public boolean isStolen() {
        return isStolen;
    }

    public void setStolen(boolean isStolen) {
        this.isStolen = isStolen;
        notifyObservers();
    }

    public void attachObserver(VehicleObserver observer) {
        observers.add(observer);
    }

    public void detachObserver(VehicleObserver observer) {
        observers.remove(observer);
    }

    public void notifyObservers() {
        for (VehicleObserver observer : observers) {
            observer.update(this);
        }
    }
}

// Observer Interface
interface VehicleObserver {
    void update(FamilyCar vehicle);
}

// Concrete Observer: Police
class Police implements VehicleObserver {
    @Override
    public void update(FamilyCar vehicle) {
        if (vehicle.isStolen()) {
            System.out.println("Police: " + vehicle.getPlateNumber() + " has been reported stolen!");
        } else {
            System.out.println("Police: " + vehicle.getPlateNumber() + " is no longer reported stolen.");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // Create a vehicle
        FamilyCar vehicle = new FamilyCar("VN123", false);

        // Create a police observer
        Police police = new Police();

        // Attach the police observer to the vehicle
        vehicle.attachObserver(police);
        
        // Update the state, immediately notify the observer
        vehicle.setStolen(true); // Output: Police: VN123 has been reported stolen!
        vehicle.setStolen(false); // Output: Police: VN123 is no longer reported stolen.
        vehicle.setStolen(true); // Output: Police: VN123 has been reported stolen!

        // Detach the police observer from the vehicle
        vehicle.detachObserver(police);

        // Report the vehicle as stolen again (but the police won't be notified this time)
        vehicle.setStolen(true);
    }
}

In this example, the Subject (Vehicle interface) defines the interface for the subject, which is the object being observed. It declares methods for attaching, detaching, and notifying observers.

Concrete Subject (FamilyCar class) implements the Vehicle interface and represents the concrete subject. It maintains a list of observers and provides methods for attaching, detaching, and notifying observers. When the state of the vehicle (e.g., stolen status) changes, it notifies all the attached observers.

Observer (VehicleObserver interface) defines the interface for the observer objects. It declares the update method that is called by the subject to notify the observer of state changes.

  1. Concrete Observer (Police class) implements the VehicleObserver interface and represents a concrete observer. It defines the behavior that should be executed when it is notified by the subject. In this example, the Police observer prints a message indicating whether the vehicle has been reported stolen or is no longer reported stolen.

In the Main class, the code demonstrates the usage of the Observer pattern. It creates a FamilyCar object representing a vehicle and a Police object representing the observer. The Police observer is attached to the FamilyCar vehicle using the attachObserver method. Then, the stolen status of the vehicle is changed using the setStolen method, which triggers the notification to the attached observer. The observer updates its state accordingly and prints a message.

The Observer pattern allows objects to establish a one-to-many dependency relationship, where multiple observers are interested in the state changes of a subject. The subject maintains a list of observers and notifies them automatically when its state changes, without the need for observers to actively poll for updates.

  • Multiple observers can be attached to a FamilyCar object.

Observer Design Pattern Summary

Use the Observer pattern when changes to the state of one object may require changing other objects, and the actual set of objects is unknown beforehand or changes dynamically.

  • You can often experience this problem when working with classes of the graphical user interface. For example, you created custom button classes, and you want to let the clients hook some custom code to your buttons so that it fires whenever a user presses a button.
    • The Observer pattern lets any object that implements the subscriber interface (VehicleObserver) subscribe for event notifications in publisher objects. You can add the subscription mechanism to your buttons, letting the clients hook up their custom code via custom subscriber classes.

Use the pattern when some objects in your app must observe others, but only for a limited time or in specific cases.

  • The subscription list is dynamic, so subscribers (Police) can join or leave the list whenever they need to.

This can be implemented as follow:

class Police implements VehicleObserver {
    private Vehicle subject;

    public Police(Vehicle subject) {
        this.subject = subject;
    }

    public void subscribe() {
        subject.attachObserver(this);
        System.out.println("Police: Subscribed to " + subject.getPlateNumber());
    }

    public void unsubscribe() {
        subject.detachObserver(this);
        System.out.println("Police: Unsubscribed from " + subject.getPlateNumber());
    }

    @Override
    public void update(FamilyCar vehicle) {
        if (vehicle.isStolen()) {
            System.out.println("Police: " + vehicle.getPlateNumber() + " has been reported stolen!");
        } else {
            System.out.println("Police: " + vehicle.getPlateNumber() + " is no longer reported stolen.");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // Create a vehicle
        FamilyCar vehicle = new FamilyCar("VN123", false);

        // Create a police observer
        Police police = new Police(vehicle);
        vehicle.attachObserver(police);

        // Report the vehicle as stolen
        vehicle.setStolen(true); // Output: Police: VN123 has been reported stolen!
        vehicle.setStolen(false); // Output: Police: VN123 is no longer reported stolen.
        vehicle.setStolen(true); // Output: Police: VN123 has been reported stolen!

        // Self-Unsubscribe from the FamilyCar object
        police.unsubscribe(); // Output: Police: Unsubscribed from VN123

        // Report the vehicle as no longer stolen (but the police won't be notified)
        vehicle.setStolen(false);
    }
}