Decoupling JavaScript Vs. The World

Decoupling JavaScript Vs. The World

My presentation from Web Rebels in Norway

* Note: all illustrations of Scott Pilgrim are copyright of Brian O'Malley.

96270e4c3e5e9806cf7245475c00b275?s=128

Addy Osmani

June 05, 2012
Tweet

Transcript

  1. FEATURING ARTWORK FROM BRIAN O’MALLEY

  2. I WORK AT AS A DEVELOPER PROGRAMS ENGINEER 50% ENGINEER

    50% TECHNICAL WRITER WHO AM I?
  3. When I tell people I work at Google they think

    my day is spent like this:
  4. The reality is my days are mostly spent like this

    ; ) Bugs Bugs Bugs
  5. I’M KIDDING OF COURSE! I love working for Google <3

  6. I <333 WRITING

  7. When I tell people I’m getting published, they think the

    quality of my books are like this:
  8. The reality is the quality is a little more like

    this.
  9. CONTRIBUTED TO: CREATED: TodoMVC jQuery UI Bootstrap jQuery Plugin Patterns

    Backbone Paginator Feature detection tests Documentation, Writing, Triage Latest Todo application
  10. SO... DECOUPLING

  11. DE..WHAT?

  12. decoupling [ˌdiːˈkʌpəәngl] separating (joined or coupled subsystems) enabling them to

    exist and operate separately oooOoh.
  13. Coupling

  14. Coupling is when two (or more) pieces of code must

    know each other in order to function correctly. We consider this bad when this relationship is unnecessary.
  15. decoupling

  16. Decoupling is when we remove these unnecessary relationships, allowing units

    of code to function independently or free of dependencies they don’t need to be directly tied to.
  17. BUT REALLY..DECOUPLING IS AMAZING.

  18. SOME ARCHITECTURE IDEAS FROM GOOGLE PROJECTS. GUESS WHERE YOU’LL FIND

    DECOUPLING USED A LOT?
  19. Use MVP as it supports decoupled development between multiple developers

    simultaneously Google Docs (GWT)
  20. Applications should be able to function independently or with other

    apps in a container (think widgets) Google Docs (GWT)
  21. None
  22. Many should be able to work on a codebase without

    being limited by dependencies Another Google project.
  23. Use deferred JavaScript loading to only load chunks/modules as they're

    needed MUCH HARDER IF THEY’RE NOT DECOUPLED Old GMail, Google Docs
  24. WE SEEM TO LIKE DECOUPLING.

  25. GMail & Calendar: Closure AdWords & AdSense: Google Web Toolkit

    Google Docs/Drive : Closure + GWT BTW, in case you were wondering..we use
  26. USE CLOSURE/GWT IF POSSIBLE OR BORROW SOME OF THESE IDEAS

    TO WORK WITH YOUR OWN STACK IF YOU’RE NOT READY. (AND MAYBE TRY OUT SOME OTHER AWESOME ONES TOO)
  27. DOES YOUR CURRENT ARCHITECTURE.. MODULES WIDGETS MVC LIBRARIES TOOLKITS APPLICATION

    CORE TEMPLATES
  28. ALLOW YOU TO CONTROL THE LIFECYCLE OF YOUR WIDGETS? MAXIMIZE

    THE REUSABILITY OF COMPONENTS AND WIDGETS? ALLOW YOU TO EASILY SWITCH LIBRARIES YOU USE WITHOUT MASSIVE CODE RE-WRITES? STILL WORK IF A SPECIFIC MODULE FAILS?
  29. I HOPE YOU SAID NO. LET’S CHANGE THAT!

  30. DECOUPLING

  31. COMPONENT-LEVEL DECOUPLING

  32. CHARTS FORMULA EDITOR GRID SPREADSHEET WIDGET THEORETICAL EXAMPLE

  33. TEXT FORMATTER TABLE AUTOCOMPLETE GRID SEARCH/FILTER REUSABLES

  34. TEXT FORMATTER MEDIA MANAGER AUTOCOMPLETE STATUS UPDATE REUSABLES

  35. PROMOTES REUSABILITY LESS COUPLING = EASIER REUSE

  36. MODULE-LEVEL DECOUPLING BASIC CONCEPTS

  37. LET’S GIVE DESIGN PATTERNS SOME LOVE

  38. NOT THIS KIND OF LOVE.

  39. OBSERVER (PUB/SUB) BASIC CONCEPTS

  40. JQUERY: COUPLED, DIRECT CALLS $.when( $.ajax("WebRebels/mail.php") ) .then(function( ajaxArgs ){

    var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Now has to worry about all of these next... // Display notification and preview modals.show('New Message', data); // Play new message sound audioManager.play('newMessage'); // Update the unread message count messages.increaseMessageCount('unread'); });
  41. JQUERY: DECOUPLED, PUBLISH/SUBSCRIBE // Dialogs $.subscribe('newMessage', function(){ modals.show('New Message', data);

    }); // Sounds $.subscribe('newMessage', function(){ audioManager.play('newMessage'); }); $.when( $.ajax("WebRebels/mail.php") ) .then(function( ajaxArgs ){ var jqXHR = ajaxArgs[2], data = jqXHR.responseText; // Tell the application there is a new message, share the data $.publish('newMessage', data); // Other modules can now just listen out for 'newMessage' });
  42. PUB/SUB WITH RADIO.JS myFunction = function(){ /*...*/ } // create

    topic called newMessage and subscribe myFunction radio('newMessage').subscribe(myFunction); // publish to the topic newMesage radio('newMessage').broadcast(data); // unsubscribe myFunction from the topic newMessage radio('newMessage').unsubscribe(myFunction); // do all of the above in one line via chaining radio('newMessage') .subscribe(myFunction) .broadcast(data) .unsubscribe(myFunction);
  43. PUB/SUB IN NODE WITH FAYE var Faye = require('faye'), server

    = new Faye.NodeAdapter({mount: '/'}); server.listen(8000); // Create a client var client = new Faye.Client('http://localhost:8000/'); // Define subscribers client.subscribe('/messages', function(message) { alert('Got a message: ' + message.text); }); // Publish messages client.publish('/messages', { text: 'Hello Web Rebels!' });
  44. OR WITH BACKBONE EVENTS var myObject = {}; _.extend( myObject,

    Backbone.Events ); // Subscribe myObject.on('eventName', function( msg ) { console.log( 'triggered:' + msg ); }); // Publish myObject.trigger('eventName', 'some event'); // You can even namespace: person.on('change:name change:age', function( msg ) { console.log('name and age changed'); });
  45. LOGICALLY DECOUPLE OBJECTS GENERATING EVENTS FROM THOSE REACTING TO THEM

    NEW MESSAGE ‘HELLO WORLD’ THERE’S A NEW MESSAGE!
  46. BAKED INTO MVC, MV* MODELS VIEWS CONTROLLERS MODELS CAN BE

    OBSERVED FOR CHANGES
  47. FACADE PATTERN

  48. HIDES IMPLEMENTATION LEVEL DETAILS FOR LIBRARIES

  49. ENABLES COMPLEXITY BEHIND THE SCENES

  50. $( el ).css() $( el ).animate(..) $( el ).attributes( key,

    value ) ... EXAMPLES YOU USE ALL THE TIME
  51. A SIMPLE FACADE W/ THE MODULE PATTERN var module =

    (function() { var myPrivates = { i:5, get : function() { console.log('current value:' + this.i); }, set : function( val ) { this.i = val; }, run : function() { console.log('running'); } }; return { facade : function( args ) { myPrivates.set(args.val); myPrivates.get(); if ( args.run ) { myPrivates.run(); } } } }()); Badly named, but all of this is private. This facade is all the public interact with. It’s a limited API that takes input like the below and does more work behind the scenes. module.facade({run: true, val:10}); // outputs current value: 10, running
  52. JQUERY’S $(document).ready(..) // We just use this.. $(document).ready(...) or //

    But its a lot more complex than that (this only reflects half of what it does), but the beauty is that we don’t need to worry about the implementation-level details. (function (window) { // Define a local copy of $ var $ = function (callback) { readyBound = false; $.isReady = false; if (typeof callback == 'function') { DOMReadyCallback = callback; } bindReady(); }, // Use the correct document accordingly with window argument (sandbox) document = window.document, readyBound = false, DOMReadyCallback = function () {}, // The ready event handler DOMContentLoaded; // Is the DOM ready to be used? Set to true once it occurs. $.isReady = false; // Handle when the DOM is ready var DOMReady = function () { // Make sure that the DOM is not already loaded if (!$.isReady) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if (!document.body) { setTimeout(DOMReady, 13); return; } // Remember that the DOM is ready
  53. MEDIATOR PATTERN

  54. TOWER HANDLES WHAT PLANES CAN TAKE OFF OR LAND ALL

    COMMUNICATION DONE FROM PLANES TO TOWER, NOT PLANE TO PLANE A CENTRALISED CONTROLLER IS KEY TO SUCCESS HERE ANALOGY: AIR TRAFFIC CONTROL
  55. VERY SIMILAR TO..

  56. ALLOWS MODULES TO BROADCAST OR LISTEN FOR NOTIFICATIONS A MEDIATOR:

  57. MEDIATOR RESPONSIBLE FOR LETTING COLLEAGUES KNOW OF CHANGES. LIKE PUB/SUB,

    BUT: COLLEAGUES DON’T HAVE TO HOLD REFERENCES TO OTHER COLLEAGUES.
  58. Mediator.js - solid mediator implementation // We just use this..

    $(document).ready(...) or // But its a lot more complex than that (this only reflects half of what it does), but the beauty is that we don’t need to worry about the implementation-level details. (function (window) { // Define a local copy of $ var $ = function (callback) { readyBound = false; $.isReady = false; if (typeof callback == 'function') { DOMReadyCallback = callback; } bindReady(); }, // Use the correct document accordingly with window argument (sandbox) document = window.document, readyBound = false, DOMReadyCallback = function () {}, // The ready event handler DOMContentLoaded; // Is the DOM ready to be used? Set to true once it occurs. $.isReady = false; // Handle when the DOM is ready var DOMReady = function () { // Make sure that the DOM is not already loaded if (!$.isReady) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). if (!document.body) { setTimeout(DOMReady, 13); return; } // Remember that the DOM is ready http://thejacklawson.com/ Mediator.js/ http://thejacklawson.com/Mediator.js/
  59. Mediator.js function Subscriber(fn, options, context){ if (!this instanceof Subscriber) {

    return new Subscriber(fn, context, options); }else{ this.id = guidGenerator(); this.fn = fn; this.options = options; this.context = context; this.channel = null; } }; Publish: function(data){ for(var y = 0, x = this._callbacks.length; y < x; y++) { if(!this.stopped){ var callback = this._callbacks[y], l; if(callback.options !== undefined && typeof callback.options.predicate === "function"){ if(callback.options.predicate.apply(callback.context, data)){ callback.fn.apply(callback.context, data); } }else{ callback.fn.apply(callback.context, data); } } l = this._callbacks.length; if(l < x) y--; x = l; }
  60. MODULES

  61. MODULE PATTERN OBJECT LITERALS AMD ASYNCHRONOUS MODULES - UNTIL WE

    GET ES HARMONY SIMULATED PRIVACY MODULAR OBJECTS
  62. Simple AMD Module // Call define() with an optional ID,

    a dependency array and a factory function allowing us to alias dependencies loaded for usage define(optionalId, ['underscore', 'backbone'], function (_, Backbone) { // Return a defined module return function () {}; });
  63. AMD With Dynamic Dependencies define(function ( require ) { var

    isReady = false, foobar; // note the inline require within our module definition require(['foo', 'bar'], function (foo, bar) { isReady = true; foobar = foo() + bar(); }); // we can still return a module return { isReady: isReady, foobar: foobar }; });
  64. CommonJS: require() and exports // package/lib is a dependency we

    require var lib = require('package/lib'); // some behaviour for our module function foo(){ lib.log('hello world!'); } // export (expose) foo to other modules exports.foo = foo;
  65. ECMAScript 6 Module /* * ES6 Modules (which you can

    use with Google Traceur) * http://traceur-compiler.googlecode.com/git/demo/repl.html */ module Profile { // module code export var firstName = 'Alex'; export var lastName = 'Russell'; export var year = 1973; } module ProfileView { import Profile.{firstName, lastName, year}; function setHeader(element) { element.textContent = firstName + ' ' + lastName; } // rest of module }
  66. BRAINSTORM

  67. LOOSELY COUPLED ARCHITECTURE CONTROLLABLE INDEPENDENT WIDGETS FLEXIBILITY TO CHANGE (E.G

    LIBRARIES) WHAT ARE WE LOOKING FOR?
  68. NOTIFY US WHEN SOMETHING INTERESTING HAPPENS DON’T TOUCH ANYTHING THEY

    DON’T HAVE TO CAN’T CAUSE THE ENTIRE APP TO STOP WORKING WIDGETS
  69. SOLUTION: COMBO! + +

  70. APPLICATION CORE (MEDIATOR) WIDGETS (UI BLOCKS) MODULES (REQUIREJS 2.0 +

    AMD) SANDBOX (FACADE) AURA
  71. DEMO

  72. JUST VIEWING THE SLIDES? NO WORRIES! GRAB THE CODE BELOW.

    http://bit.ly/backbone-aura
  73. 1. THE APPLICATION CORE USING THE MEDIATOR PATTERN MANAGES THE

    WIDGET LIFECYCLE: STARTS, STOPS, RESTARTS IF NECESSARY
  74. USING THE MEDIATOR PATTERN

  75. Thanks RequireJS 2.0! shim config: no need to use patched

    Backbone.js or Underscore.js anymore! require.undef(): Unload specific modules
  76. None
  77. None
  78. 1. THE APPLICATION CORE USING THE MEDIATOR PATTERN REACTS TO

    ACTIONS PASSED BACK FROM A SANDBOX (FACADE) - HANDLES LOGIC
  79. None
  80. 1. THE APPLICATION CORE USING THE MEDIATOR PATTERN HANDLES ERROR

    DETECTION AND MANAGEMENT
  81. None
  82. 2. THE SANDBOX USING THE FACADE PATTERN AN ABSTRACTION OF

    THE CORE THAT’S AN API FOR COMMON TASKS, USED BY MODULES
  83. None
  84. None
  85. 2. THE SANDBOX USING THE FACADE PATTERN INTERFACE FOR ENSURING

    MODULES DON’T DIRECTLY ACCESS THE CORE/LIBRARIES
  86. None
  87. 2. THE SANDBOX USING THE FACADE PATTERN PERMISSIONS MANAGER, SECURING

    WHAT MODULES CAN/CAN’T ACCESS
  88. None
  89. None
  90. 3. WIDGETS UNIQUE (INDEPENDENT) BLOCKS OF FUNCTIONALITY FOR YOUR APPLICATION

  91. None
  92. None
  93. None
  94. None
  95. 3. WIDGETS CAN BE EASILY STARTED, STOPPED OR RESTARTED

  96. 3. WIDGETS SUBSCRIBE TO NOTIFICATIONS OF INTEREST, RAPIDLY REACT TO

    CHANGES
  97. 3. WIDGETS NOTIFY THE APP WHEN SOMETHING INTERESTING HAPPENS (PUBLISH)

  98. None
  99. None
  100. 4. MODULES UNITS OF CODE THAT CAN BE LOADED, UNLOADED

    OR EASILY RE-USED USING AMD
  101. THAT’S IT FOR AURA!

  102. REMEMBER, COUPLING IS BAD.

  103. Blog Twitter GitHub http://addyosmani.com @addyosmani http://github.com/addyosmani DECOUPLING IS AWESOME.