I actually wanted to write about PubSub alone: it’s a fascinating design pattern to me however, the thought occurred to me, why not write a design patterns’ series? It’ll be good knowledge for me and give good information. So here goes the first: PubSub.
Introduction
The pattern works using a middleman; an agent that bridges publishers and subscribers. Publishers are the objects that fire events when they are done doing some processing and subscribers are objects who wish to be notified when the publisher is done – i.e. they are interested in the work of publishers.
A good example is that of a radio station where people tune in to their favourite programs. The publisher has no knowledge of the subscribers or what programs they are listening to; he only needs to publish his program. Subscribers too have no way of knowing what goes on during program production; when their favourite program is running, they can respond by tuning in or informing a friend.
PubSub achieves very loose coupling: instead of looking for ways to link up two discrete systems; you can have one hand off messages and have the second part consume these messages.
Advantages
1. Loose coupling
Publishers do not need to know about the number of subscribers, what topics a subscriber is listening to or how subscribers work; they can work independently and this allows you to develop both separately without worrying about ripple effects, state or implementation.
2. Scalability
PubSub allows systems to scale however it buckles under load.
3. Cleaner Design
To make the best use of PubSub, you have to think deeply about how the various components will interact and this usually leads to a clean design because of the emphasis on decoupling and looseness.
4. Flexibility
You don’t need to worry about how the various parts will fit; just make sure they agree to the one contract or the other i.e. publisher or subscriber.
5. Easy Testing
You can easily figure out if a publisher or subscriber is getting the wrong messages.
Disadvantages
PubSub’s greatest strength – decoupling – is also its biggest disadvantage.
- The middleman might not notify the system of message delivery status; so there is no way to know of failed or successful deliveries. Tighter coupling is needed to guarantee this.
- Publishers have no knowledge of the status of the subscriber and vice versa. How can you be sure everything is alright on the other end? You never can say…
- As the number of subscribers and publishers increase, the increasing number of messages being exchanged leads to instabilities in this architecture; it buckles under load.
- Intruders (malicious publishers) can invade the system and breach it; this can lead to bad messages being published and subscribers having access to messages that they shouldn’t normally receive.
- Update relationships between subscribers and publishers can be a thorny issue – afterall they don’t know about each other.
- The need for a middleman/broker, message specification and participant rules adds some more complexity to the system.
Conclusion
There are no silver bullets but this is one excellent way of designing loosely coupled systems. The same concepts drive RSS, Atom and PubSubHubbub.
PubSub Example (JavaScript)
var makePubSub = function () { | |
var callbacks = {}, | |
publish = function (){ | |
//Turn arguments object into real array | |
var args = Array.prototype.slice.call(arguments, 0); | |
//Extract the event name which is the first entry | |
var ev = args.shift(); | |
//Return if callbacks object doesn't contain | |
//any entry for event | |
var list, i, l; | |
if(!callbacks[ev]) { return this; } | |
list = callbacks[ev]; | |
//Invoke the callbacks, passing in the rest of parameters | |
for (i=0, l=list.length; i<l; i=i+1){ | |
list[i].apply(this, args); | |
} | |
return this; | |
}, | |
subscribe = function (ev, callback){ | |
//Check if ev is already registered | |
// If it isn't create an array entry for it | |
if(!callbacks[ev]){ | |
callbacks[ev] = []; | |
} | |
callbacks[ev].push(callback); | |
return this; | |
}; | |
return {pub: publish, sub: subscribe}; | |
}; | |
test = makePubSub() | |
test.sub('alert', function(){alert('hello');}) | |
test.pub('alert') |
return {pub: publish, sub: subscribe};
Benefits of dynamic language. I wonder how many interfaces and class it will take to write the same in my beloved Java.
LikeLike
:D.
Static languages are so difficult to use for rapid prototyping;
LikeLike
Great one sir…
I’ll be waiting for your next post in the series.. :)
LikeLike
No sir please :)
Now; that’s a motivating challenge to take on.
LikeLike
This dude is fantastic! One of these days I should reach your level..but the question is when I’m at your level, will it be your current level or your level then? *smile*
LikeLike
Bros oh; you’re a great coder too. :D
LikeLike
Great article. Simple yet so powerful. Small typo for callbacks variable declaration as object.
LikeLike
Thanks a lot.
I don’t understand your point about the callbacks variable; can you please expatiate?
LikeLike
it’s an object because he needs Map-like access to the specific alert by name (so instead of callbacks[0], he wants callbacks[‘alert’], which will bring back a list of events to pop-off and process).
LikeLike
Thanks for explaining Anatoly! :)
LikeLike
Amazing post, amazing cons and pros about this patterns. Thank you!
LikeLike
Thanks for the feedback! Glad you liked it!!
LikeLike