Search code examples
javadesign-patternsobserver-pattern

Avoid exposing implementation details when implementing an observer pattern in Java?


I'm coming to Java from C# where events are first class citizens and event handlers can be private methods. I'm now working on a Java project where I of course need to use an Observer pattern to implement eventing.

Since the Observer needs to expose its listener/handler callback methods to the Observable class, it seems that this is exposing implementation of the details of the Observer to other unconcerned classes. I can set the access to these methods to the package level which would keep these implementation details hidden to consumers of my package, but it still has a bad "smell" to me.

Is this just something I need to grit my teeth and bare or is there a better way?


Solution

  • With the Observer pattern your classes which are observing do not need to expose their implementations. Often your class which is notifying other classes will have its own associated interface for those other classes to implement.

    public interface Observer
    {
        public void handleSomeEvent(Object someObjectOfImportance);
    }
    
    public class Observable
    {
        public void register(Observer observer);
    }
    

    Any class can implement the Observer interface and register itself without exposing any implementation details. It does expose that it implements an interface, but that does not specify how it implements it. Alternatively, you could provide an anonymous implementation.

    public class SomeObserverImplementation implements Observer
    {
        public void handleSomeEvent(Object someObjectOfImportance)
        {
            // I can do whatever I want here
        }
    }
    

    Update If you are concerned that your class now is exposing a new interface there are some ways to work around it. One is to create an anonymous implementation. Another is that you can have a private internal class which implements the Observable interface.

    public class IDontWantOthersToKnowIObserve
    {
        private class HiddenObserver implements Observer
        {
            public void handleSomeEvent(Object someObjectOfImportance)
            {
               ...
            }
        }
    
        ... in some method ...
        theObservable.register(myHiddenObserver);
    }
    

    To use an anonymous implementation:

    theObservable.register(new Observer()
                {
                    public void handleSomeEvent(Object someObjectOfImportance)
                    {
                                    // ...
                    }
                });