Codementor Events

Design Patterns: Observer Pattern

Published Oct 03, 2017Last updated Oct 10, 2017
Design Patterns: Observer Pattern

originally posted on my blog at henricodesjava.blog

Hi everyone! Chapter Two of Head First Design Patterns: A Brain-Friendly Guide is all about the Observer Pattern.

They make note of how frequently this pattern is used and how fundamental it is. They also allude to the idea that future patterns in this book are based on this and other pattern combinations. So, let's talk about the Observer Pattern.

The problem this pattern is trying to solve is about communication and management. A simple abstract example given in the text is about a newspaper publishing company and its subscribers.

Technically, this relationship can be categorized as a ONE-TO-MANY relationship — one publisher and many subscribers. The text uses the term ‘Subject‘ to refer to the publisher, and the term ‘Observer‘ to refer to the subscribers.

Next, I’ll give a more concrete example. I’ll use an example slightly different from the text to help illustrate the idea.

Let's say we are a finance startup, and we have an idea for a smartwatch app that can communicate with all of your financial institutions to give you a bird’s eye view of your finances.

For example, it can show you the balances of your checking account or the prices of stocks you own. What we have access to is a class that can return these values for us — let's call this class FinancialData. For the purposes of this example, we don’t care how it retrieves this information.

Screen Shot 2017-09-30 at 12.28.17 AM.png

Below is a naive implementation of our smartwatch app, along with an explanation. Of course, all of this is just pseudo-code, not actual smartwatch code.

Screen Shot 2017-09-30 at 1.24.50 AM.png

There are a few things wrong with this implementation.

First, we are in violation of a design principle from back in Chapter One — identifying the aspect of your application that varies and separating them from what stays the same. The lines with the green arrows represent a different screen on the watch.

In the future, we might want to add more screens. When that times comes, changes to this class are unfortunately unavoidable. The lines with red arrows are retrieving financial data. While this too can change, it would be a change caused by an API change and not a change on our client-side code.

We are also in violation of another design principle — programming to an interface, not an implementation. If only there were a way we can neatly separate code that changes, from code that doesn’t, and also doesn't program to an implementation? Introducing, the Observer Pattern!

The Observer Pattern: Defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

We will accomplish this and introduce loose coupling via the use of interfaces. Let's illustrate this definition with a class diagram.

Screen Shot 2017-09-30 at 10.56.06 AM

Now, with these class diagrams as a guide, let's take a look at an implementation for our smartwatch app. First, the two interfaces are below.

public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
} 
public interface Observer {
    public void upate(Object obj);
}

Next, let's take a look at our new FinancialData class that implements the Subject interface.

public class FinancialData implements Subject {
   private ArrayList observers;
 
   private float checkingBalance;
   private float savingsBalance;
 
   private float stockValue;
   private float fourOnekValue;
 
   private float weekSpending;
   private float monthSpending;
 
   public FinancialData(){
      observers = new ArrayList();
   }
 
   public void registerObserver(Observer o){
      observers.add(o);
   }
 
   public void removeObserver(o){
      int i = observers.indexOf(o);
      if(i > 0){
         observers.remove(i);
      }
   }
 
   public void notifyObservers(){
      for(int i = 0; i<observers.size(); i++){
         Observer observer = (Observer)observers.get(i);
         observer.update(this);
      }
   }
    
   public void measurementsChanged(){
      notifyObservers();
   }
 
   public void setNewBalanceData(float val1, float val2){
      this.checkingBalance = val1;
      this.savingsBalance = val2;
      measurementsChanged();
   }
    
   public void setNewInvestmentData(float val1, float val2){
      this.stockValue = val1;
      this.fourOnekValue = val2;
      measurementsChanged();
   }
    
   public void setNewSpendingData(float val1, float val2){
      this.weekSpending = val1;
      this.monthSpending = val2;
      measurementsChanged();
   }
   // other FinancialData methods here
   // including getter methods for balance, investment, and spending values
}

Now, let's take a look at one of our observers, the Balance smartwatch screen.

public class BalanceScreen implements Observer {
   private float checkingBal;
   private float savingsBal;
   private Subject financialData;
 
 
   public BalanceScreen(Subject financialData){
      this.financialData = financialData;
      financialData.registerObserver(this);
   }
 
   public void update(Object obj){
      if( obj instanceof FinancialData){
         FinancialData fd = (FinancialData) obj;
         this.checkingBal = fd.getCheckingBalance();
         this.savingsBal = fd.getSavingsBalance();
      }
      display();
   }
   // other BalanceScreen methods here
}

Finally, let's take a look at a program to glue all of this together.

public class SmartWatch {
    public static void main(String[] args){
        FinancialData financialData = new FinancialData();
 
        BalanceScreen balanceScreen = new BalanceScreen(financialData);
        InvestmentScreen investmentScreen = new InvestmentScreen(financialData);
        SpendingScreen spendingScreen = new SpendingScreen(financialData);
 
        // not using an actualy finance api, so we set values manually
        financialData.setNewBalanceData(348.12, 3600.87);
        financialData.setNewInvestmentData(899.12, 45000.65);
        financialData.setNewSpendingData(210.34, 677.45);
 
        // at this point we should see print statements for all 3 screens
    }
}

There you have it!

We've implemented the interfaces and created some code to test it.

Now, please note that I haven’t actually gotten this code running, so there might be a bug or two in these lines.

For a future post, I’ll have to create a project and push this working code up on GitHub. Thanks for reading 👋🏽

(Originally posted at my personal blog: https://henricodesjava.blog)

Discover and read more posts from Henri
get started
post commentsBe the first to share your opinion
Show more replies