The observer pattern is one of the most used patterns when developing current web technologies.
For instance, in the interface when you change a value, such as a date, and something changes immediately in another place of the UI, it is the Observer pattern working behind the scenes.
When you make a REST API in an angular application, you register a method that will process the result after receiving the response, it is the Observer pattern.
In applications with buttons and dropdowns that affect other controls, it is again the Observer pattern.
what is the main idea of this pattern
You want to use this pattern when a component should be immediately notified when something changes its state or a specific behaviour happens.
By the way, this pattern is not the same as “publisher-subscriber”, but that’s a subject for another article.
An example
To avoid the common examples, imagine we have a class that every time a record is sent to a database a set of other classes want to know about, for instance, a logger and an audit class.
Let’s write a simple draft to understand a possible implementation.
- We have a
CustomerDao
, the class responsible to send data to the database and the class that has information of utility to others classes - We have an interface for
Subscriber
to allow any class to be notified if it implements this interface - And we have 2 other classes to be Subscibers
DatbaseLoggerObserver
andDatabaseAuditObserver
, to be notified from theCustomerDao
every time a new Customer is inserted, but since they are decoupled from theCustomerDao
they can receive from anywhere
Usually a possible flux would be
1. create the instances of the classes
2. Add the subscribers to the CustomerDao
3. Execute!
this is just a snippet to understand the pattern, a production code can and should do so much better. For instance you can extract the subscribers list and operations to add and remove them to another class, and so on
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class CustomerDao { List<subscriber> subs = new ArrayList<>(); public void addSubscriber(Subscriber sub) { subs.add(sub); } public void insertCustomer(Customer customer) { // ...some useful code. About database // notify the subscribers subs.forEach(s -> s.notify(customer.getEmail()); } } interface Subscriber { void notify(String email); } class DatabaseLoggerObserver implements Subscriber { public void notify(String email) { System.out.println(“I will log to another database the email: “ + email); } } class DatabaseAuditObserver implements Subscriber { public void notify(String email) { System.out.println(“I will audit and do some verifications and alerts for “ + email); } } |
Advantages and disadvantages
Advantages
- Allows to send data to other objects without knowing them
- If it is well implemented the objects interact in a loosely coupled way
- Observers can be added and removed at any time without changing the code
Disadvantages
- Look for performance issues since each notification waits for each observer
- What happens if an observer throws an exception?
Conclusions
This is a great pattern and is widely used at the code level, such as events in the interface, trigger behaviour in specific classes and methods and so on.
This pattern is not a good option if you need this behaviour at the architecture level, since it doesn’t have mechanisms regarding performance, scalability, reliability, etc. For these cases consider to use publisher-subscriber