Design Patterns In Action Part-1: Implementing Observer Pattern for an Ecommerce Order Completion Workflow
Observer Pattern
Definition
The Observer pattern is a design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes. The purpose of the Observer pattern is to establish a one-to-many relationship between objects, so that when one object changes its state, all its dependents are notified and updated automatically.
Why we use Observer Pattern?
We use the Observer pattern when we want to decouple an object’s behavior from the objects it depends on. This promotes loose coupling between objects and makes it easier to modify and maintain the code in the future. By implementing the Observer pattern, we can create a flexible and extensible system that can respond to state changes in a consistent and predictable way. Some examples of where the Observer pattern is commonly used include user interface updates, event-driven systems, and asynchronous programming.
Lets implement Observer Pattern in a practical scenerio.
Our Scenario
Suppose you have an e-commerce platform and you want to send notifications to the customer and vendor when an order is completed. You also want to maintain a log of all order status changes and send an invoice to the customer when the order is completed.
To implement this functionality, you create an OrderStatus
class that represents the status of an order. You also create a Logging
class, an Email
class, and an SmsNotification
class that will handle the respective tasks of logging order status changes, sending an invoice to the customer's email address, and sending notifications to the customer and vendor's mobile numbers.
The OrderStatus
class is observable, which means that the Logging
, Email
, and SmsNotification
classes observe it. When the status of an order changes to "completed", the Logging
class logs the order history, the Email
class sends an invoice to the customer's email address, and the SmsNotification
class sends notifications to the customer and vendor's mobile numbers.
When the Main
method is executed, it creates instances of the OrderStatus
, Logging
, Email
, and SmsNotification
classes and attaches the observers to the OrderStatus
instance. It then updates the status of the order to "completed". This triggers the observers to perform their respective tasks.
Implementation
IObserver Interface
The IObserver
interface defines the Update
method, which will be implemented by the observer classes.
public interface IObserver
{
void Update(string message);
}
Observable Class
The Observable
class is an abstract class that defines the methods for attaching and detaching observers, as well as notifying the observers when the state of the object changes.
public abstract class Observable
{
private List<IObserver> observers = new List<IObserver>();
public void Attach(IObserver observer)
{
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
protected void Notify(string message)
{
foreach (var observer in observers)
{
observer.Update(message);
}
}
}
The observers
list contains all the observers that are attached to the object. The Attach
method adds an observer to the list, while the Detach
method removes an observer from the list. The Notify
method iterates over the observers in the list and calls the Update
method on each observer, passing in the message
parameter.
OrderStatus Class
The OrderStatus
class extends the Observable
class and represents the status of an order.
public class OrderStatus : Observable
{
private string status;
public string Status
{
get { return status; }
set
{
status = value;
Notify(status);
if (status == "completed")
{
NotifyCompleted();
}
}
}
private void NotifyCompleted()
{
Notify("Order completed");
}
}
The status
field stores the current status of the order, and the Status
property is used to get or set the status. When the status is set, the Notify
method is called to notify the observers that the status has changed. If the status is "completed", the NotifyCompleted
method is called, which in turn calls the Notify
method with the string "Order completed".
Logging Class
The Logging
class implements the IObserver
interface and represents a logging system that logs order history.
public class Logging : IObserver
{
public void Update(string message)
{
// Do something with the message in the logging class
AppendOrderHistory(message);
}
private void AppendOrderHistory(string message)
{
// Append the message to the order history log
}
}
The Update
method is called by the Observable
object when the status of the order changes. The AppendOrderHistory
method is called to append the message to the order history log.
Email Class
The Email
class implements the IObserver
interface and represents an email notification system that sends an invoice to the customer when an order is completed.
public class Email : IObserver
{
public void Update(string message)
{
// Do something with the message in the email class
if (message == "Order completed")
{
SendInvoiceToCustomer();
}
}
private void SendInvoiceToCustomer()
{
// Send the invoice to the customer's email address
}
}
The Update
method is called by the Observable
object when the status of the order changes. If the message is "Order completed", the SendInvoiceToCustomer
method is called to send an invoice to the customer.
SmsNotification Class
The SmsNotification
class implements the IObserver
interface and represents an SMS notification system that sends notifications to the customer and vendor when an order is completed.
public class SmsNotification : IObserver
{
public void Update(string message)
{
// Do something with the message in the SMS notification class
if (message == "Order completed")
{
SendSMSNotificationToCustomer();
SendSMSNotificationToVendor();
}
}
private void SendSMSNotificationToCustomer()
{
// Send a notification to the customer's mobile number
}
private void SendSMSNotificationToVendor()
{
// Send a notification to the vendor's mobile number
}
}
The Update
method is called by the Observable
object when the status of the order changes. If the message is "Order completed", the SendSMSNotificationToCustomer
and SendSMSNotificationToVendor
methods are called to send notifications to the customer and vendor, respectively.
Main Method
The Main
method creates instances of the OrderStatus
, Logging
, Email
, and SmsNotification
classes, attaches the observers to the OrderStatus
instance, and updates the status of the order to "completed".
static void Main(string[] args)
{
var orderStatus = new OrderStatus();
var logging = new Logging();
var email = new Email();
var smsNotification = new SmsNotification();
orderStatus.Attach(logging);
orderStatus.Attach(email);
orderStatus.Attach(smsNotification);
// Update the status of the order to "completed"
orderStatus.Status = "completed";
Console.ReadLine();
}
This shows that the observers were notified when the status of the order changed to “completed”. The Logging
class would append the message to the order history log, the Email
class would send an invoice to the customer's email address, and the SmsNotification
class would send notifications to the customer and vendor's mobile numbers.
Conclusion
In conclusion, the Observer pattern is a powerful design pattern that enables objects to communicate and respond to state changes in a flexible and decoupled manner. By decoupling objects through the use of observers and subjects, we can create systems that are more flexible, extensible, and easier to maintain. The Observer pattern is widely used in many programming contexts, including user interfaces, event-driven systems, and asynchronous programming.