5

I was checking this example of Observer Pattern.

https://www.tutorialspoint.com/design_pattern/observer_pattern.htm

It seems that there is a circular reference when Observer Pattern is used.

import java.util.ArrayList;
import java.util.List;
public class Subject {

   private List<Observer> observers = new ArrayList<Observer>();
   private int state;

   public int getState() {
      return state;
   }

   public void setState(int state) {
      this.state = state;
      notifyAllObservers();
   }

   public void attach(Observer observer){
      observers.add(observer);      
   }

   public void notifyAllObservers(){
      for (Observer observer : observers) {
         observer.update();
      }
   }    
}


public class BinaryObserver extends Observer{

  public BinaryObserver(Subject subject){
      this.subject = subject;
      this.subject.attach(this);
   }

   @Override
   public void update() {
      System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); 
   }
}

I am not posting all the other code, but from the initial look it seems that such design will almost always have a circular reference. Am I misunderstanding anything here?

Thanks,

3
  • 2
    Where do you see a circular reference? Commented Dec 19, 2018 at 10:41
  • Subject depends on Observer and BinaryObserver depends on Subject. However that's not circular. Subject would have to depend on BinaryObserver first.
    – Neil
    Commented Dec 19, 2018 at 12:17
  • Are you worried that GCs can't collect cycles? They can.
    – Caleth
    Commented Nov 18, 2022 at 15:43

4 Answers 4

5

Yes, you are right. Both objects know each other if that is what your are referring to. The important thing is what they know about each other. While the BinaryObserver knows the Subject in detail the Subject knows only Observers. To be more precise the Subject doesn't know the details about your BinaryObserver it can only send messages to it that are defined in the Observer class. In that way Subject is decoupled from the details of your BinaryObserverclass.

To sum up, while the coupling of the BinaryObserver to the Subject is very high the coupling in the opposite direction is weak.

3
  • I have seen some designs e.g. GSON api where GSON building fails if there are circular references (I think the exception is StackOverflowException) - but surely, this doesn't mean the Observer Pattern is wrong and cannot use utilities such as GSON?
    – ha9u63a7
    Commented Dec 19, 2018 at 12:02
  • 1
    @hagubear This is because you cannot serialize an instance which has a reference to itself or through some chain of dependencies, itself, otherwise a tree representing said data would never end. This is more of a technical issue related to serialization and not general best practice (though admittedly avoiding circular references is best practice anyway).
    – Neil
    Commented Dec 19, 2018 at 13:20
  • I hope I could read your answer early when I learning Observer Pattern...
    – NingW
    Commented Apr 4, 2019 at 6:27
2

Update: As JimmyJames pointed out in the comments, the question is about circular references and not about circular dependencies


Original Answer, where I thought we were discussing circular dependencies:

Actually, because of polymorphism, there is no circular dependency here. Let's look closer at the dependencies to see why:

  • BinaryObserver depends on Subject and Observer
  • Subject depends on Observer
  • Observer doesn't have any dependencies.

By having the Subject depend on the Observer interface rather than any concrete implementation, the Dependency Inversion Principle is being followed. This principle states:

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
  2. Abstractions should not depend on details. Details should depend on abstractions.

So, although it may look like circular dependency at a glance, it's really just polymorphing a concrete implementation that depends on Subject (BinaryObserver) to an interface with no Subject dependency (Observer), and injecting that polymorphed object into Subject's constructor. Thus, no circular dependency.

4
  • The BinaryObserver instance has a reference to the Subject instance. The Subject instance has a reference to the BinaryObserver instance via a list -> circular dependency.
    – JimmyJames
    Commented Dec 19, 2018 at 19:01
  • A Circular Dependency isn't about references to instances on runtime, it's about classes (or modules) depending on each other in order to function. Subject does not require BinaryOperator to function.
    – Adam Bates
    Commented Dec 19, 2018 at 19:31
  • 1
    OK, fair point on terminology but the question is about circular references not dependencies. It doesn't appear to have been edited.
    – JimmyJames
    Commented Dec 20, 2018 at 15:21
  • Ah, I missed that. Thanks for pointing out, I'll update my answer.
    – Adam Bates
    Commented Dec 20, 2018 at 23:19
1

You can avoid the circular reference with a slight change to the Observer interface:

interface Observer {
  void update(Subject subject);
}

The notifyAll becomes:

public void notifyAllObservers(){
  for (Observer observer : observers) {
     observer.update(this);
  }
}

There are potentially some tradeoffs, but in the example you have shown, you would only need a single Observer object for all the Subjects in the application. If you do it as the tutorial shows, you need one for every Subject. That's a lot of extra objects hanging around just to keep track of a reference which is easily passed in.

Another issue with the Observable pattern is that you can end up with memory leaks because the observed object is holding references to the observers. Usually the pattern includes a remove method but consider using WeakReference to allow objects to automatically stop listening when they fall out of scope.

0

Simple answer

It's not a cyclic problem; it's a different type of coupling issue because BinaryObserver is a concrete class.

Detailed answer

The answer below is taken from the book Java Application Architecture by Kirk Knoernschild.

Break cycle at the class level

enter image description here

It's not a cyclic problem; it's a different type of coupling issue BinaryObserver is a concrete class. (page 52)

Break cycle at the module level

enter image description here

To have an acyclic dependency between modules, we must move Subject and Observer in the same module. (page 54, figure 4.4 ans figure 4.5)

Conclusion

  • Observer design pattern has acyclic dependencies between classes
  • Obersver design pattern has acyclic dependencies between modules

The type of coupling

In the schema 1 and 2 we interting the relation, dependency inversion.

Interving the relationships allows us to deploy package2 module independent of the package1 module. But I'd like to explore another option [...] the ability to test and deploy the package2 module independently.

I can test and deploy package1 module independently. But if I want to test and deploy both modules independently [...] I need to completely eliminate the relationship altogether. (page 123 and figure 8.4)

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.