$30 off During Our Annual Pro Sale. View Details »

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.

Addy Osmani

June 05, 2012
Tweet

More Decks by Addy Osmani

Other Decks in Programming

Transcript

  1. FEATURING ARTWORK FROM BRIAN O’MALLEY

    View Slide

  2. I WORK AT
    AS A DEVELOPER PROGRAMS ENGINEER
    50% ENGINEER 50% TECHNICAL WRITER
    WHO AM I?

    View Slide

  3. When I tell people I work at Google they think my day is spent like this:

    View Slide

  4. The reality is my days are mostly spent like this ; )
    Bugs
    Bugs Bugs

    View Slide

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

    View Slide

  6. I <333 WRITING

    View Slide

  7. When I tell people I’m getting published, they think the quality of my books are like this:

    View Slide

  8. The reality is the quality is a little more like this.

    View Slide

  9. CONTRIBUTED TO: CREATED:
    TodoMVC
    jQuery UI Bootstrap
    jQuery Plugin Patterns
    Backbone Paginator
    Feature detection tests
    Documentation, Writing, Triage
    Latest Todo application

    View Slide

  10. SO... DECOUPLING

    View Slide

  11. DE..WHAT?

    View Slide

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

    View Slide

  13. Coupling

    View Slide

  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.

    View Slide

  15. decoupling

    View Slide

  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.

    View Slide

  17. BUT REALLY..DECOUPLING IS AMAZING.

    View Slide

  18. SOME ARCHITECTURE IDEAS FROM
    GOOGLE PROJECTS.
    GUESS WHERE YOU’LL FIND DECOUPLING USED A LOT?

    View Slide

  19. Use MVP as it supports decoupled
    development between multiple developers
    simultaneously
    Google Docs (GWT)

    View Slide

  20. Applications should be able to function
    independently or with other apps in a
    container (think widgets)
    Google Docs (GWT)

    View Slide

  21. View Slide

  22. Many should be able to work on a codebase
    without being limited by dependencies
    Another Google project.

    View Slide

  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

    View Slide

  24. WE SEEM TO LIKE DECOUPLING.

    View Slide

  25. GMail & Calendar: Closure
    AdWords & AdSense: Google Web Toolkit
    Google Docs/Drive : Closure + GWT
    BTW, in case you were wondering..we use

    View Slide

  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)

    View Slide

  27. DOES YOUR CURRENT ARCHITECTURE..
    MODULES
    WIDGETS
    MVC
    LIBRARIES
    TOOLKITS
    APPLICATION CORE
    TEMPLATES

    View Slide

  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?

    View Slide

  29. I HOPE YOU SAID NO.
    LET’S CHANGE THAT!

    View Slide

  30. DECOUPLING

    View Slide

  31. COMPONENT-LEVEL
    DECOUPLING

    View Slide

  32. CHARTS
    FORMULA EDITOR
    GRID
    SPREADSHEET
    WIDGET
    THEORETICAL EXAMPLE

    View Slide

  33. TEXT
    FORMATTER
    TABLE
    AUTOCOMPLETE
    GRID SEARCH/FILTER
    REUSABLES

    View Slide

  34. TEXT
    FORMATTER
    MEDIA MANAGER
    AUTOCOMPLETE
    STATUS UPDATE
    REUSABLES

    View Slide

  35. PROMOTES REUSABILITY
    LESS COUPLING = EASIER REUSE

    View Slide

  36. MODULE-LEVEL
    DECOUPLING
    BASIC CONCEPTS

    View Slide

  37. LET’S GIVE DESIGN
    PATTERNS SOME LOVE

    View Slide

  38. NOT THIS KIND OF LOVE.

    View Slide

  39. OBSERVER (PUB/SUB)
    BASIC CONCEPTS

    View Slide

  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');
    });

    View Slide

  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'
    });

    View Slide

  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);

    View Slide

  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!'
    });

    View Slide

  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');
    });

    View Slide

  45. LOGICALLY DECOUPLE OBJECTS
    GENERATING EVENTS
    FROM THOSE REACTING
    TO THEM
    NEW MESSAGE
    ‘HELLO WORLD’
    THERE’S A NEW
    MESSAGE!

    View Slide

  46. BAKED INTO MVC, MV*
    MODELS VIEWS CONTROLLERS
    MODELS CAN BE OBSERVED FOR CHANGES

    View Slide

  47. FACADE PATTERN

    View Slide

  48. HIDES IMPLEMENTATION
    LEVEL DETAILS FOR LIBRARIES

    View Slide

  49. ENABLES COMPLEXITY BEHIND
    THE SCENES

    View Slide

  50. $( el ).css()
    $( el ).animate(..)
    $( el ).attributes( key, value )
    ...
    EXAMPLES YOU USE ALL THE TIME

    View Slide

  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

    View Slide

  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

    View Slide

  53. MEDIATOR PATTERN

    View Slide

  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

    View Slide

  55. VERY SIMILAR TO..

    View Slide

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

    View Slide

  57. MEDIATOR RESPONSIBLE FOR LETTING
    COLLEAGUES KNOW OF CHANGES.
    LIKE PUB/SUB, BUT:
    COLLEAGUES DON’T HAVE TO HOLD
    REFERENCES TO OTHER COLLEAGUES.

    View Slide

  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/

    View Slide

  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;
    }

    View Slide

  60. MODULES

    View Slide

  61. MODULE PATTERN
    OBJECT LITERALS
    AMD
    ASYNCHRONOUS MODULES - UNTIL WE GET ES HARMONY
    SIMULATED PRIVACY
    MODULAR OBJECTS

    View Slide

  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 () {};
    });

    View Slide

  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
    };
    });

    View Slide

  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;

    View Slide

  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
    }

    View Slide

  66. BRAINSTORM

    View Slide

  67. LOOSELY COUPLED
    ARCHITECTURE
    CONTROLLABLE
    INDEPENDENT
    WIDGETS
    FLEXIBILITY TO
    CHANGE
    (E.G LIBRARIES)
    WHAT ARE WE LOOKING FOR?

    View Slide

  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

    View Slide

  69. SOLUTION: COMBO!
    + +

    View Slide

  70. APPLICATION CORE
    (MEDIATOR)
    WIDGETS
    (UI BLOCKS)
    MODULES
    (REQUIREJS 2.0 + AMD)
    SANDBOX
    (FACADE)
    AURA

    View Slide

  71. DEMO

    View Slide

  72. JUST VIEWING THE SLIDES? NO
    WORRIES! GRAB THE CODE BELOW.
    http://bit.ly/backbone-aura

    View Slide

  73. 1. THE APPLICATION CORE
    USING THE MEDIATOR PATTERN
    MANAGES THE WIDGET
    LIFECYCLE: STARTS, STOPS,
    RESTARTS IF NECESSARY

    View Slide

  74. USING THE MEDIATOR PATTERN

    View Slide

  75. Thanks RequireJS 2.0!
    shim config: no need to use patched
    Backbone.js or Underscore.js anymore!
    require.undef(): Unload specific modules

    View Slide

  76. View Slide

  77. View Slide

  78. 1. THE APPLICATION CORE
    USING THE MEDIATOR PATTERN
    REACTS TO ACTIONS PASSED
    BACK FROM A SANDBOX
    (FACADE) - HANDLES LOGIC

    View Slide

  79. View Slide

  80. 1. THE APPLICATION CORE
    USING THE MEDIATOR PATTERN
    HANDLES ERROR DETECTION
    AND MANAGEMENT

    View Slide

  81. View Slide

  82. 2. THE SANDBOX
    USING THE FACADE PATTERN
    AN ABSTRACTION OF THE
    CORE THAT’S AN API FOR
    COMMON TASKS, USED BY
    MODULES

    View Slide

  83. View Slide

  84. View Slide

  85. 2. THE SANDBOX
    USING THE FACADE PATTERN
    INTERFACE FOR ENSURING
    MODULES DON’T DIRECTLY
    ACCESS THE CORE/LIBRARIES

    View Slide

  86. View Slide

  87. 2. THE SANDBOX
    USING THE FACADE PATTERN
    PERMISSIONS MANAGER,
    SECURING WHAT MODULES
    CAN/CAN’T ACCESS

    View Slide

  88. View Slide

  89. View Slide

  90. 3. WIDGETS
    UNIQUE (INDEPENDENT)
    BLOCKS OF FUNCTIONALITY
    FOR YOUR APPLICATION

    View Slide

  91. View Slide

  92. View Slide

  93. View Slide

  94. View Slide

  95. 3. WIDGETS
    CAN BE EASILY STARTED,
    STOPPED OR RESTARTED

    View Slide

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

    View Slide

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

    View Slide

  98. View Slide

  99. View Slide

  100. 4. MODULES
    UNITS OF CODE THAT CAN
    BE LOADED, UNLOADED OR
    EASILY RE-USED
    USING AMD

    View Slide

  101. THAT’S IT FOR AURA!

    View Slide

  102. REMEMBER, COUPLING IS BAD.

    View Slide

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

    View Slide