BLOG

We write about great things, you know.

Reinventing brands with high value ideas

Notification Center Pattern

By: Diego Marafetti

Aug 1, 2013 • codingNo Comments

One of the most useful software design patterns we have used in our software team is the Notification Center. This pattern is a variation of the  Observer Pattern and it is based on the Notification center of Mozilla with some additional modifications specific to our own implementation.

The problem

As the User Interface grows in complexity, an event may generate changes in its component parts. Programmatically, if a component receives an event (either from the user or some other source) and another component must change its state because of this, then the first component should send a message to the second component notifying it of this requirement. This would imply that the sender maintains a strong reference to the receiver.

If we multiply this relation several times you can find that many of these components are cross-referenced throughout the application in the case of a complex user interface. This could result in Javascript code becoming unmanageable and result in many bugs and a very complex debugging process.

Let’s imagine a user interface showing a directory tree where each node has a list of images that can be loaded in a preview window. There is also a toolbar with some options to edit and manage the image (cut, rotate, erase, etc.). The image below illustrates a possible representation of this scenario:

nc-wireframe

Now let’s examine some use cases related to this scenario.

The first event that the user generates is a click on a given directory. This will display the images in that directory that are listed under that directory tree branch. The user can then select an image and the image preview is generated for the user and displayed.


// filename: Tree.js

onNodeClick: function(dirNode) {

dirNode.listFiles();
 }

onFileNodeClick: function(fileNode) {

    var filename = fileNode.getFilename();
    this.previewWidget.openFile(filename);
    this.directoryWidget.show();
 }
 


Another use case could be when the user wants to rotate the image by selecting the appropriate option. The image and it’s orientation would then need to be updated. Finally, what if the user wants to delete the image? The preview screen and the directory tree should refresh in this scenario.


// Filename: Optionbar.js

onRotateClick: function() {

     this.previewWidget.rotate();
 }

onRemoveImageClick: function() {

    var filename = this.previewWidget.getCurrentFile();
    this.previewWidget.closeFile();
    this.directoryWidget.removeFile(filename);
 }
 


If we draw a network diagram to show the relationship between each component, where the lines represent the relationships between the components, we can see that a lot of the components are interconnected.

nc-grafo

The purpose of using this Notification Center is to reduce the amount of references between objects.

The solution is to unify the message strategy for a single object so that it is responsible for sending and receiving messages. We may think of this as a messaging channel that is integral to the entire system, where each of these represents an event and where the objects that are listening are able to change its state depending on the event that interests or is relevant to them.

These messages or events no longer have a single receiver and instead all messages become available and sent to those recipients who need the information. This would be like a broadcast sent to a group of objects in which it’s implementation is unknown by the sender.

This action will decouple visual elements that don’t need to know about each other. The exception to this rule would be a composition of objects.

[User Interface Component Model]: In this article, when I speak of a “component”, I am talking about reusable elements that compose the user interfaces. A component can be simple, such as a button, or can be compound, such as a table, composed of multiple components.

Our goal

* To separate the UI from the business logic.

* Decouple different UI components.

* Reduce the code complexity.

How would we implement this solution?

One solution we considered was keeping an internal table of topics as well as a collection of objects that are subscribed to one of those topics. When the notification process happens, it will connect to subscribers directly using the name of the topic as the index. Then, it will send the appropriate message.

Our Notification Center may differ a little bit from Mozilla’s. First we want all subscribers that receive a notification to be only those who have subscribed to the relevant topic, but also be notified to a callback function related to that topic. Otherwise, we would have to add a condition within the callback function asking whether that topic is the one we are subscribed to. This is because an observer is able to observe or subscribe to more than one topic at the same time. To avoid this situation we use a calling convention where the function’s name is actually the topic’s name. For instance:

topic ==> "itemSelected" ==> callback ==> "onItemSelected"


This convention says that every observer subscribed to a Topic must declare a function called “on” + “topic” (lower-case) in its interface.

Notification Center Class

In our example, each UI component subscribed to a topic would be an Observer of any Topic within the system. The interface of our notification system, in its most basic form would implements these methods:

 /**
 * @param anObserver any object implementing the topic/function interface
 *
 * @param aTopic the topic name. Is unique in the hole system
 **/
 addObserver: function(anObserver, aTopic);
 


In turn, any component can work as a subject in this relationship as well. On an event, the component is able notify to all observers that the event was raised.

 /**
 * @param aTopic event name to be notify.
 * @param someDataEs. Some data to be sent.
 *
 *
 **/
 notify: function(aTopic, someData);
 


 Also, any component can unsubscribe itself from the notification system using this function call:

 /**
 * @param anObserver reference to the object to be removed
 * @param aTopic topic name
 **/
 removeObserver: function(anObserver, aTopic);
 


Let’s look at an example of how to use this solution. In this case the Image’s preview component is subscribed to the “imageSelected” event and provides a notification that an image was selected. Pay attention to the absence of a reference to the object that selects the image:


// Filename: ImagePreview.js
/**
 * Class constructor or init code
 **/
init: function() {

     NotificationCenter.addObserver(this, ‘imageSelected’);
}
/**
 * The user has selected an item from the tree
**/
onImageSelected: function(imageUri) {

     this.loadImage(imageUri);
}


The notification for a file selection does not care about the destination since it is not it’s responsibility:


// Filename: Tree.js

/**
 * The user has clicked a tree's node
 **/
onFileNodeClick: function(fileNode) {

    NotificationCenter.notify(‘imageSelected’, fileNode.getFileUri());
}


This framework has been used successfully in many large web applications. It was also migrated to Actionscript 3 for a Playbook development and used in a Java Swing application. Other frameworks like Cocoa in iOS applications already have some variant of this solution internally such as the NSNotificationCenter (this is the closest to our implementation).

Tags: ,

Leave a Reply

Your email address will not be published. Required fields are marked *


× one = 8

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>