Observer pattern Hell

Hi guys,

In the last couple of days I have been struggling with a pretty simple implementation of observer pattern. I tought that was an easy way to hook an object to another and notify it without defining specific delegates in the subject class.

The implementation is very simple, we have two pretty standard interfaces:

public interface ISubject 
{

	 void Register(IObserver observer);

	 void Unregister(IObserver observer);

	 void Notify();

}

public interface IObserver
{
	void getUpdate();
}

and the actual classes are even simpler:

public class Menu : GameMenubase, ISubject
 {

private List<IObserver> observers = new List<IObserver>();
	
		public void OnMenuOpen()
		{
			
			Notify();
			
		}
		
		public void Register(IObserver observer)
		{
			if(!observers.Contains(observer))
				observers.Add(observer);
		}
		
		public void Unregister(IObserver observer)
		{
			if(observers.Contains(observer))
				observers.Remove(observer);
		}

		
		public void Notify()
		{ 
			foreach(IObserver observer in observers)
				observer.getUpdate();
		}
}

public  class Listener : MonoBehaviour,IObserver 
{	
	
	private ISubject subject
	{
		get
		{
			return (ISubject) GameManager.Instance.Menu;
		}
	}
	
	public virtual void Start()
	{
		RegisterThis ();
	}

	
	public void getUpdate()
	{
		// do stuff
	}

	void RegisterThis ()
	{
		if (subject == null)
			return;
		subject.Register (this);
	}
}

The Subject is an hidden menu with a reference in the GameManager and is disabled at game start. When I press a button the Menu is dispayed and its children (the listener : iObserver) are enabled too. The problem is that when the button call the method:

EnableMenu()
{
NguiTools(GameManager.Instance.Menu,true);
GameManager.Instance.Menu.OnMenuOpen();
}

I get no observer in the list! I guess that the problem here is that NGUI enable the menu but the observer are still not active when we call OnMenuOpen (there are a lot of them nested in a quite deep hierachy).

I tried a couple of solution but none of them is really good:

  1. Delay the OnMenuOpen => quite stupid because force a unecessary slowness in the code even if you have one observer
  2. Turn the logic upside down: the menu search for the observer in the hierachy and if found any register it => the code cycle trough a lot of objects even if they are not relevant and moreove Unity is not able to search on disabled gameobject (GetComponentinchildren doesnt work)
  3. Switch to a hybrid Mediator: A singleton with lazy load. When someone needs to change its state will call the Mediator and ask for a specific signature. At same time if some specific event occur in the system the Mediator will now (if relevant) and notify the specific objects. => I love this solution but it is huge to implement.

I’m pretty much out of option. The last solution I can imagine is some sort of proxy that will not call OnMenuOpen() untill the full hierachy is enabled. But even this solution smells a lot to me. Any advice?

Unity won’t call the observer’s Start method until the currently-executing entry point (whatever called EnableMenu) returns. You could use OnEnable instead of Start, which should get called straight away.

If you generally want to be tolerant of an observer connecting late, and want it to still get an immediate notification, then you could just make Register call Notify directly on any new observer - or make the observer itself just know that it needs to immediately notify itself because it has missed anything that has happened already.

Also, though not the cause of the problem, you might want to use events instead - your Register/Unregister/getUpdate structure is mostly just a manual implementation of C#'s events, and it would be clearer if you just used them.

Man thank you for the advice. I will start right away with the events implementation and then use OnEnable. I will post my findings after a couple of tests.

After a couple of try I have this now in the Observer class:

private ISubject subject
	{
		get
		{
			return (ISubject) MenuManager.Instance.activeMenu;
		}
	}
	
	public virtual void OnEnable()
	{
		subject.OnPublishing += OnPublishing;
	}

	public virtual void Disable()
	{
		subject.OnPublishing -= OnPublishing;
	}

The pros are:

  • clean code
  • small classes
  • very easy to extend

The cons are:

  • The problem is still there.