Design Patterns - Observer

Design Patterns - Observer

A simple Python code example to illustrate the Observer Pattern for an ETL-like scenario. The "Extract Microservice" observes changes in a data source and notifies its registered observers (data extractors) to perform data extraction. We'll use a basic Publisher-Subscriber pattern to demonstrate the concept of the Observer Pattern.

Observer interface

class Observer:
    def update(self, data):
        pass

Extract Microservice (Subject)

class ExtractMicroservice:
    def __init__(self):
        self.observers = []

    def register_observer(self, observer):
        self.observers.append(observer)

    def unregister_observer(self, observer):
        self.observers.remove(observer)

    def notify_observers(self, data):
        for observer in self.observers:
            observer.update(data)

    def start_extraction(self):
        # Simulate data extraction process
        data = ["Data 1", "Data 2", "Data 3", "Data 4"]

        # Notify all registered observers about the new data
        self.notify_observers(data)

Extractor (Observer)

class DataExtractor(Observer):
    def update(self, data):
        print("DataExtractor received new data:", data)
        # Perform data extraction operations
        # (In a real-world scenario, this could involve data retrieval from external sources)

Main function to demonstrate the Observer Pattern

if __name__ == "__main__":
    # Create an instance of the Extract Microservice
    extract_microservice = ExtractMicroservice()

    # Create DataExtractor instances as observers
    data_extractor1 = DataExtractor()
    data_extractor2 = DataExtractor()

    # Register the observers with the Extract Microservice
    extract_microservice.register_observer(data_extractor1)
    extract_microservice.register_observer(data_extractor2)

    # Start the data extraction process (simulated)
    extract_microservice.start_extraction()

    # Output:
    # DataExtractor received new data: ['Data 1', 'Data 2', 'Data 3', 'Data 4']
    # DataExtractor received new data: ['Data 1', 'Data 2', 'Data 3', 'Data 4']

SOLID Principles

The application of the SOLID principles in the given code example results in a well-structured design that is modular, extensible, and easy to maintain. The Observer Pattern helps to decouple the components, making the code more flexible and adaptable to changes without the need for extensive modifications.

Single Responsibility Principle (SRP)

The ExtractMicroservice class has a single responsibility, which is to manage the extraction process and notify its registered observers about the new data. It doesn't handle the data extraction or processing itself. The DataExtractor class also adheres to SRP by having a single responsibility of processing the data received from the ExtractMicroservice.

Open/Closed Principle (OCP)

The code demonstrates the Open/Closed Principle as the ExtractMicroservice class is open for extension and can easily accommodate new data extractors without modifying its code. This is achieved by using the Observer Pattern, allowing new DataExtractor classes to be added as observers without changing the ExtractMicroservice.

Liskov Substitution Principle (LSP)

In the code, there is no explicit inheritance or subclassing. However, the Liskov Substitution Principle is implicitly followed, as the DataExtractor class implements the Observer interface, which defines the update() method. This ensures that any concrete implementation of the Observer interface can be substituted with another without affecting the behavior of the ExtractMicroservice.

Interface Segregation Principle (ISP)

The Observer Pattern follows the Interface Segregation Principle, as the Observer interface only declares the update() method. This means that concrete observers (such as DataExtractor) are not forced to implement unnecessary methods they don't require.

Dependency Inversion Principle (DIP)

The Dependency Inversion Principle is demonstrated through the loose coupling between the ExtractMicroservice and the DataExtractor. The ExtractMicroservice depends on the Observer interface rather than on specific concrete implementations. This allows the ExtractMicroservice to work with any class that implements the Observer interface, promoting flexibility and maintainability.

Popular posts from this blog

Atom - Jupyter / Hydrogen

Design Patterns

Robson Koji Moriya disambiguation name