What is Observers Design Pattern


Introduction:

The observer design pattern is a behavioral pattern that is used to establish a one-to-many dependency between objects. In this pattern, when one object changes its state, all the other dependent objects get notified and updated automatically. The Observer pattern is used to design systems that require a loosely-coupled architecture, where changes in one part of the system do not affect other parts of the system.

This pattern is also known as the Publish-Subscribe pattern or the Event-Listener pattern. The Observer pattern is widely used in GUI programming, where a change in one part of the user interface needs to be reflected in other parts of the interface.

Implementing Observer pattern in Java:

To implement the Observer pattern in Java, we need to define two types of objects: the Observable and the Observer. The Observable is the object that is being observed, while the Observer is the object that is observing the Observable.

Let's create a simple example to demonstrate how to implement the Observer pattern in Java. Suppose we have a class called WeatherStation that reports the current temperature to its observers. The WeatherStation class is the Observable, and the observers are the classes that want to be notified when the temperature changes. We can define the WeatherStation class as follows:

import java.util.Observable;

public class WeatherStation extends Observable {
    private int temperature;

    public void setTemperature(int temperature) {
        this.temperature = temperature;
        setChanged();
        notifyObservers();
    }

    public int getTemperature() {
        return temperature;
    }
}

The Observable class is a built-in class in Java that provides methods to register and notify observers. To make the WeatherStation class observable, we extend the Observable class. We define a private field called temperature to store the current temperature, and two methods: setTemperature() to update the temperature and notify the observers, and getTemperature() to retrieve the current temperature.

To create an observer class, we define an interface called Observer. The Observer interface has only one method called update() that takes an Observable object and an Object argument. The Observable object is the object that has changed, and the Object argument is any additional data that the Observable object wants to pass to the observers. We can define the Observer interface as follows:

import java.util.Observer;

public interface WeatherObserver extends Observer {
}

Now, let's create an observer class called TemperatureDisplay that displays the current temperature. The TemperatureDisplay class implements the Observer interface and defines the update() method to display the current temperature. We can define the TemperatureDisplay class as follows:

import java.util.Observable;
import java.util.Observer;

public class TemperatureDisplay implements WeatherObserver {
    private Observable observable;

    public TemperatureDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherStation) {
            WeatherStation weatherStation = (WeatherStation) observable;
            int temperature = weatherStation.getTemperature();
            System.out.println("Temperature: " + temperature);
        }
    }
}

The TemperatureDisplay class has a constructor that takes an Observable object as an argument. In the constructor, we register the TemperatureDisplay object as an observer of the Observable object by calling the addObserver() method of the Observable object.

The update() method is called when the Observable object changes its state. In the update() method, we check if the Observable object is an instance of the WeatherStation class. If it is, we cast the Observable object to a WeatherStation object and retrieve the current temperature by calling the getTemperature() method of the WeatherStation object. We then display the current temperature using the System.out.println() method.

Advantages of Observer pattern:

1. Loosely Coupled Architecture: The Observer pattern provides a loosely coupled architecture, where the Observable and the Observer objects are independent of each other. This means that changes to one object do not affect the other object, and the system can be easily extended without affecting the existing code.

2. Separation of Concerns: The Observer pattern separates the concerns of the Observable and the Observer objects. The Observable object is responsible for maintaining the state of the system, while the Observer objects are responsible for reacting to changes in the state of the system.

3. Easy to Implement: The Observer pattern is easy to implement, and it provides a standardized way of implementing communication between objects. This makes the code easier to read and maintain.

4. Reusability: The Observer pattern promotes reusability of code. The same Observable object can be observed by multiple Observer objects, and the same Observer object can observe multiple Observable objects.

Disadvantages of Observer pattern:

1. Performance Overhead: The Observer pattern can have performance overhead, especially when there are a large number of Observers. Each Observer object needs to be notified when the state of the Observable object changes, and this can result in a lot of overhead.

2. Complexity: The Observer pattern can add complexity to the system, especially when there are multiple Observers and multiple Observable objects. This can make the system harder to understand and debug.

Implementing Observer pattern in C:

In C, we can implement the Observer pattern using function pointers. Instead of defining an Observer interface, we define a callback function that is called when the state of the Observable object changes. Let's create a simple example to demonstrate how to implement the Observer pattern in C.

#include <stdio.h>

typedef struct {
    int temperature;
    void (*notify)(int);
} WeatherStation;

void setTemperature(WeatherStation *weatherStation, int temperature) {
    weatherStation->temperature = temperature;
    weatherStation->notify(weatherStation->temperature);
}

void temperatureDisplay(int temperature) {
    printf("Temperature: %d\n", temperature);
}

int main() {
    WeatherStation weatherStation = {0, temperatureDisplay};
    setTemperature(&weatherStation, 25);
    return 0;
}

In this example, we define a struct called WeatherStation that has a temperature field and a notify function pointer. The notify function pointer is called when the temperature changes.

We define a setTemperature() function that updates the temperature and calls the notify function pointer. The notify function pointer is passed the temperature as an argument.

We define a temperatureDisplay() function that displays the temperature using the printf() function.

In the main() function, we create a WeatherStation object and set its temperature to 25. This calls the temperatureDisplay() function and displays the temperature.

Implementing Observer pattern in Python:
In Python, we can implement the Observer pattern using the built-in Observer module. The Observer module provides an Observable class and an Observer class. We can define a class that extends the Observable class and define a class that extends the Observer class. Let's create a simple example to demonstrate how to implement the Observer pattern in Python.

from observer import Observable, Observer

class WeatherStation(Observable):
    def __init__(self):
        super().__init__()
        self.temperature = 0
    
    def setTemperature(self, temperature):
        self.temperature = temperature
        self.notifyObservers(self.temperature)

class TemperatureDisplay(Observer):
    def update(self, observable, arg):
        if isinstance(observable, WeatherStation):
            temperature = observable.temperature
            print("Temperature: ", temperature)

weatherStation = WeatherStation()
temperatureDisplay = TemperatureDisplay()
weatherStation.addObserver(temperatureDisplay)
weatherStation.setTemperature(25)

In this example, we import the Observable and Observer classes from the observer module. We define a class called Weather 

Station that extends the Observable class. The WeatherStation class has a temperature field and a setTemperature() method that updates the temperature and calls the notifyObservers() method to notify the Observer objects.

We define a class called TemperatureDisplay that extends the Observer class. The TemperatureDisplay class has an update() method that is called when the Observable object changes. The update() method prints the temperature.

In the main code, we create a WeatherStation object and a TemperatureDisplay object. We add the TemperatureDisplay object as an Observer of the WeatherStation object using the addObserver() method. We then set the temperature of the WeatherStation object to 25, which triggers the update() method of the TemperatureDisplay object, and the temperature is printed.

Implementing Observer pattern in Java:

In Java, we can implement the Observer pattern using the built-in Observer interface and Observable class. We can define a class that extends the Observable class and define a class that implements the Observer interface. Let's create a simple example to demonstrate how to implement the Observer pattern in Java.

import java.util.Observable;
import java.util.Observer;

class WeatherStation extends Observable {
    private int temperature = 0;
    
    public void setTemperature(int temperature) {
        this.temperature = temperature;
        setChanged();
        notifyObservers(this.temperature);
    }
}

class TemperatureDisplay implements Observer {
    public void update(Observable observable, Object arg) {
        if (observable instanceof WeatherStation) {
            WeatherStation weatherStation = (WeatherStation) observable;
            int temperature = weatherStation.temperature;
            System.out.println("Temperature: " + temperature);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();
        TemperatureDisplay temperatureDisplay = new TemperatureDisplay();
        weatherStation.addObserver(temperatureDisplay);
        weatherStation.setTemperature(25);
    }
}

In this example, we define a class called WeatherStation that extends the Observable class. The WeatherStation class has a temperature field and a setTemperature() method that updates the temperature and calls the setChanged() and notifyObservers() methods to notify the Observer objects.

We define a class called TemperatureDisplay that implements the Observer interface. The TemperatureDisplay class has an update() method that is called when the Observable object changes. The update() method checks if the Observable object is an instance of the WeatherStation class, and if it is, it gets the temperature from the WeatherStation object and prints it.

In the main code, we create a WeatherStation object and a TemperatureDisplay object. We add the TemperatureDisplay object as an Observer of the WeatherStation object using the addObserver() method. We then set the temperature of the WeatherStation object to 25, which triggers the update() method of the TemperatureDisplay object, and the temperature is printed.

Advantages of Observer pattern in Java, C and Python:

1. Easy to Implement: The Observer pattern is easy to implement in Java, C, and Python. Java and Python provide built-in classes and interfaces for implementing the Observer pattern, while C can use function pointers.

2. Flexible: The Observer pattern is flexible and can be used in a wide range of applications. It can be used for event handling, UI programming, and data synchronization.

3. Scalable: The Observer pattern can be scaled to handle a large number of Observers and Observable objects.

4. Encourages Loose Coupling: The Observer pattern encourages loose coupling between objects, which makes the system more modular and easier to maintain.

Disadvantages of Observer pattern in Java, C and Python:

1. Performance Overhead: The Observer pattern can have performance overhead, especially when there are a large number of Observers. Each Observer object needs to be notified when the state of the Observable object changes, and this can be time-consuming and resource-intensive.

2. Complexity: The Observer pattern can add complexity to the code. Implementing the pattern requires creating Observable and Observer classes, and managing the registration and deregistration of Observers.

3. Potential for Memory Leaks: The Observer pattern can create memory leaks if Observers are not properly removed from the Observable object. If an Observer object is not removed from the Observable object, it will continue to receive notifications even if it is no longer needed, which can lead to memory leaks.

4. Inefficient for One-to-Many Relationships: The Observer pattern can be inefficient for one-to-many relationships, where multiple Observer objects need to receive the same notification. In this case, each Observer object needs to be notified separately, which can be inefficient.

Conclusion:

In conclusion, the Observer pattern is a powerful design pattern that can be used in a wide range of applications. It provides a way to decouple objects and make the system more modular and easier to maintain. The Observer pattern is easy to implement in Java, C, and Python, and provides flexibility and scalability. However, it can also have performance overhead, add complexity to the code, and create memory leaks if not implemented correctly. It is important to weigh the advantages and disadvantages of the Observer pattern before deciding to use it in your code.

       

Advertisements

ads