Introduction
In today's fast-paced world, data is a critical asset for everyone. Real-time notifications have become essential as they facilitate timely access to this data. In software development, ensuring efficient and prompt notifications is vital. The Observer pattern is an ideal solution for effectively managing real-time updates.
Why the Observer Pattern?
The Observer pattern is ideal for real-time notifications because it allows a subject to efficiently notify multiple observers about state changes, ensuring timely and scalable updates. This pattern promotes loose coupling, making it easy to add or remove observers without significant code modifications. The advantages of using the Observer pattern are:
- One-to-Many Relationship: A single subject can notify multiple observers (subscribers).
- Loose Coupling: Easily add or remove observers without altering the subject.
- Automatic Updates: Observers are automatically notified of changes, ensuring timely updates.
Real-Time Applications of the Observer Pattern
The Observer pattern is commonly used in:
- Weather Applications: Sending push notifications to users with weather updates.
- Social Media: Notifying users about likes, comments, or messages in real time.
- E-commerce "Notify Me" Features: Alerting users when out-of-stock items become available again.
These examples demonstrate the versatility and importance of the Observer pattern in ensuring timely and relevant notifications, which aligns perfectly with my requirements for developing a notification system for a newsletter.
My Requirements: Developing a Notification System for a Newsletter
I was assigned the task of developing a newsletter system to send email notifications after each production release. These notifications would include major release items, a link to view all released items, and instructions on how to use the new features. The goals were to create a notification system that:
- Scales Easily: Accommodates new subscribers without extensive code changes.
- Offers Flexibility: Allows subscribers to choose their preferred notification method (email).
- Ensures Efficiency: Sends timely notifications after production releases.
Solution
High-Level Overview
The Observer pattern structures the notification system with:
- Subject Interface: Defines all the necessary methods to add, remove, and notify subscribers.
- Subject or Event Source (ProductionRelease): Generates events (state changes) and maintains a list of observers interested in those events.
- Observers Interface: Defines the method to trigger the notification to its subscribers.
- Observers or Event Listeners (Subscribers): Add the observers to the observer list maintained by the Subject to receive notifications about events.
Here is the UML representation of how the observer pattern would look like:
Low-Level Implementation
Step 1: Create the Subject Interface and Implementation
Define the subject with methods to add, remove, checkForUpdates, and notify observers.
// ObservableInterface.ts
export interface ObservableInterface {
add(observer: ObserverInterface): void;
remove(observer: ObserverInterface): void;
checkForUpdates(): void;
notify(): void;
}
// ProductionRelease.ts
export class ProductionRelease implements ObservableInterface {
private observerList: ObserverInterface[] = [];
// Add a new observer to the list
add(observer: ObserverInterface): void {
this.observerList.push(observer);
}
// Remove an observer from the list
remove(observer: ObserverInterface): void {
const index = this.observerList.indexOf(observer);
if (index !== -1) {
this.observerList.splice(index, 1);
}
}
// Check for updates and notify observers
checkForUpdates(): void {
//If new release then notify the users
this.notify();
}
// Notify all observers about the update
notify(): void {
for (const observer of this.observerList) {
observer.update();
}
}
}
Step 2: Create the Observer Interface and Implementations
Define how subscribers receive notifications via email.
// ObserverInterface.ts
export interface ObserverInterface {
update(): void;
}
export class EmailSubscriberObserver implements ObserverInterface {
private emailSubscribersList: Set<string> = new Set();
subscribe(emailId: string) {
// Add the new email to the subscription list and notify the subscriber
this.emailSubscribersList.add(emailId);
this.sendEmail(emailId, 'Your newsletter subscription was successful. You will receive updates soon after the release. Stay tuned!!!');
}
// Update method called by the subject to notify the observer
update(): void {
// Fetch the list of email subscribers from the set
const emailListArray = Array.from(this.emailSubscribersList);
for (const email of emailListArray) {
this.sendEmail(email, 'Yes!!!! We have launched a new release of our software. Please click on the below link to get a detailed view of what this release is about.');
}
}
// Unsubscribe an email from the list
unSubscribe(emailId: string): void {
this.emailSubscribersList.delete(emailId);
this.sendEmail(emailId, 'You have unsubscribed from our newsletter. Please let us know if there is any feedback from your side.');
}
// Private method to simulate sending an email
private sendEmail(email: string, emailBody: string): void {
// Implement the email send logic
console.log(`Email sent to ${email}: ${emailBody}`);
}
}
Step 3: Implement Main Functionality
Instantiate the subject and observers, and trigger notifications.
// Main.ts
import { ProductionRelease } from './ProductionRelease';
import { EmailSubscriberObserver } from './EmailObserver';
import { ObservableInterface } from './ObservableInterface';
import { ObserverInterface } from './ObserverInterface';
// Create an Observable class with an object set to the instance of ProductionRelease
const productionReleaseObservable: ObservableInterface = new ProductionRelease();
// Create an Observer class object
const emailObserverObj: EmailSubscriberObserver = new EmailSubscriberObserver();
// Create all the email subscribers for the EmailSubscriberObserver
emailObserverObj.subscribe('soos@gmail.com');
emailObserverObj.subscribe('soos123@gmail.com');
emailObserverObj.subscribe('soos_other@gmail.com');
// Unsubscribe one of the subscribers
emailObserverObj.unSubscribe('soos123@gmail.com');
// Add the list of observers to the main observable object
productionReleaseObservable.add(emailObserverObj);
/* We can add more observers ex: smsObserverObj, pushNotificationObj etc
productionReleaseObservable.add(smsObserverObj);
productionReleaseObservable.add(pushNotificationObj);
*/
// Check for updates which will trigger notifications to all subscribers
productionReleaseObservable.checkForUpdates();
Output
Conclusion
The Observer pattern is a robust choice for creating a newsletter system that handles real-time notifications efficiently. Its scalability and flexibility make it ideal for managing notifications with minimal code changes.
References
- Observer Design Pattern Explanation - YouTube
- Observer Pattern from Refactor Guru - Refactoring Guru
- "Head First Design Patterns" by Eric Freeman and Elisabeth Robson
Thank you for taking the time to read this article. If you found it helpful, please give it a like. Feel free to comment if you notice anything missing or have suggestions for improvement. Your feedback will help enhance the content and benefit future readers.
Top comments (1)
Neat explanation!!