diff --git a/authors.md b/authors.md index be4922c..5946947 100644 --- a/authors.md +++ b/authors.md @@ -21,6 +21,7 @@ The following people are totally rad and awesome because they have contributed r * Frederic Hemberger * Mike Hatfield *oakraven13@gmail.com* * [Anton Rissanen](http://github.com/antris) *hello@anton.fi* +* [Julien Buty](http://github.com/nepsilon) [julien@nepsilon.net](mailto:julien@nepsilon.net) * ...You! What are you waiting for? Check out the [contributing](/contributing) section and get cracking! # Developers diff --git a/chapters/ajax/ajax_request_without_jquery.md b/chapters/ajax/ajax_request_without_jquery.md index 5956548..d95713c 100644 --- a/chapters/ajax/ajax_request_without_jquery.md +++ b/chapters/ajax/ajax_request_without_jquery.md @@ -47,7 +47,8 @@ loadDataFromServer = -> req.addEventListener 'readystatechange', -> if req.readyState is 4 # ReadyState Compelte - if req.status is 200 or req.status is 304 # Success result codes + successResultCodes = [200, 304] + if req.status in successResultCodes data = eval '(' + req.responseText + ')' console.log 'data message: ', data.message else diff --git a/chapters/arrays/max-array-value.md b/chapters/arrays/max-array-value.md index ce3adc1..25f1472 100644 --- a/chapters/arrays/max-array-value.md +++ b/chapters/arrays/max-array-value.md @@ -26,4 +26,4 @@ Alternatively, it's possible to use ES5 `reduce` method. For backward compatibil ## Discussion -`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable ammount of arguments, such as `console.log`. +`Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable amount of arguments, such as `console.log`. diff --git a/chapters/design_patterns/mediator.md b/chapters/design_patterns/mediator.md new file mode 100644 index 0000000..a80fca1 --- /dev/null +++ b/chapters/design_patterns/mediator.md @@ -0,0 +1,77 @@ +--- +layout: recipe +title: Mediator pattern +chapter: Design patterns +--- + +## Problem + +Imagine 100 airplanes that need to land in airport A, which only accept one airplane landing at a time. +First strategy is to have every plane asking all its peers for landing order. This would be a very time consuming, error prone and quite tedious task. +A better option is to have all airplanes report directly to a control tower, which determines the landing schedule for all airplanes. +Each plane then simply request landing permission from the control tower, and wait for a reply. +With the latter approach, a given plane doesn't need to know about all other planes. +All communication can now be centralized and synchronously broadcasted to all. + +Similarly, we can face this problem when developing software. +When an application is composed of several modules, each module is in charge of its own task but also dependent on the state of other modules. We can have each module listening directly to each of its dependencies, maybe through the Observer pattern, and be notified when a change occur. +The problems with this approach are the same as having airplanes listening to each other. When we have tight coupling between modules, every module has to watch out for its dependencies. +A better approach would be to introduce loose coupling of modules and centralize inter-module communication. With such an approach, each module doesn't need to know about the existence of other modules, but simply listen for events they fire. This is the mediator pattern. + +## Solution + +{% highlight coffeescript %} + +# Our mediator class +class Mediator + + constructor: -> + @channels = {} + + subscribe: (event, context, callback) => + @channels[event] or= [] + @channels[event].push callback.bind context + + publish: (event, args...) => + return false if not @channels[event] + callback(args) for callback in @channels[event] + + wrap: (obj) -> + obj.subscribe = @subscribe + obj.publish = @publish + obj + +# Instantiate our Mediator +MEDIATOR = new Mediator() + +# Module A +class ModuleA + + constructor: -> + MEDIATOR.wrap @ # We add event capabilities to our module + @subscribe 'hello', @, (payload) -> console.log "(ModuleA) Received event 'hello' with payload: #{payload}" + # The 2 lines above could also have been written: + # MEDIATOR.subscribe 'hello', @, (payload) -> console.log "Received event 'hello' with payload: #{payload}" + +# Module B +class ModuleB + + constructor: -> + MEDIATOR.wrap @ # We add event capabilities to our module + @publish 'hello', 'I am moduleB and I just published!' + # The 2 lines above could also have been written: + # MEDIATOR.subscribe 'hello', @, (payload) -> console.log "Received event 'hello' with payload: #{payload}" + +# Instantiate our 2 modules +new ModuleA() +new ModuleB() + +{% endhighlight %} + + +## Discussion + +As you can see we now have inter-module communication without having modules looking out for their dependencies. Hence, ModuleA won't fail if ModuleB isn't there and vice-versa. +Such an approach makes a lot of sense when developing applications such as [Netvibes](http://netvibes.com), [Google Ig](http://google.com/ig) or [Chartbeat](https://chartbeat.com/) dashboards. + +The `MEDIATOR.wrap` step could also be abstracted into a parent class for convenience.