On Github frnz / solid_presentation
Un conjunto de buenas prácticas de OO para preparar el código a cambiar. Mantiene el código los liviano posible, pero preparándolo para poder hacer cambios con el más mínimo esfuerzo.
No es una cantidad de métodos, más bien agrupaciones de métodos que cumplen un objetivo
Cart = function(items) {
  this.log = function(log) {
    console.log(log)
  },
  this.calculateTotal = function() {
    return items.sum("price") // sugar.js <3 <3 <3
  },
  this.displayInfo = function() {
    this.log("Total is " + this.calculateTotal())
    $("#total").html(total)
  }
}
          
          Cart = function(items, options) {
  this.log = function(log) {  
    if (options.log) {         // D: D: D:
      if (options.logActive) {
        console.log(log)
      }
    }
  },
  this.calculateSubtotal = function() {
    return items.sum("price")
  },
  this.calculateTotal = function() {...}
  this.displayInfo =  function() {
    this.log("Total is " + total)
    $("#subTotal").html(this.calculateSubtotal())
    $("#total").html(this.calculateTotal())
  }
}
          
        Cart = function(items, options) {
  this.log = function(log) {  
    if (options.log) {         // D: D: D:
      if (options.logActive) {
        console.log(log)
      }
    }
  },
  this.calculateSubtotal = function() {
    return items.sum("price")
  },
  this.calculateTotal = function() {...}
  this.displayInfo =  function() {
    this.log("Total is " + total)
    if (options.showSubtotal) { // ლ(ಠ益ಠლ) 
      $("#subTotal").html(this.calculateSubtotal())
    }
    $("#total").html(this.calculateTotal())
  }
}
          
        6
98.986
CartCalculator
  CartCalculator = function(items) { 
    this.subTotal = function() {
      return this.items.sum("price")
    }
    this.total = function() {
      return this.subTotal() + this.subTotal()*0.13 
    } 
  }
        Renderers
CartInfoRenderer = function(cartCalculator) {
  $("#total").html(cartCalculator.total()) 
}
CartPageInfoRenderer = function(cartCalculator) {
  $("#subtotal").html(cartCalculator.subTotal())
  $("#total").html(cartCalculator.total()) 
}
  
        Logger
  Logger = function() {
    this.log = function(message) {
      if (DEBUG) {
        console.log(message)
      }
    }
  }
    
        Controller
  CartController = function(options) {
    options ||= // default options
    this.render = function() {
      options.logger.log("Total is " + options.cart.total())
      options.renderer(options.cart)
    }
  }
  // Página de carrito:
  Cart({renderer: CartPageInfoRenderer}).render() 
  // Otras
  Cart({renderer: CartInfoRenderer}).render()
        Ahí afuera: Backbone patterns:
  var http = require('http');
  http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
  }).listen(1337, '127.0.0.1');
  console.log('Server running at http://127.0.0.1:1337/');
          
        
  var http = require('http');
  http.createServer(function (req, res) {
    var header=req.headers['authorization']||'',        
      token=header.split(/\s+/).pop()||'',            
      auth=new Buffer(token, 'base64').toString(),    
      parts=auth.split(/:/),                        
      username=parts[0],
      password=parts[1];
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
  }).listen(1337, '127.0.0.1');
  console.log('Server running at http://127.0.0.1:1337/');
          
        
  var http = require('http');
  http.createServer(function (req, res) {
    var header=req.headers['authorization']||'',        
      token=header.split(/\s+/).pop()||'',            
      auth=new Buffer(token, 'base64').toString(),    
      parts=auth.split(/:/),                        
      username=parts[0],
      password=parts[1];
    // CSRF CODEZ @.@
    // Incio tiempo de respuesta
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
    // Log tiempo de respuesta
  }).listen(1337, '127.0.0.1');
  console.log('Server running at http://127.0.0.1:1337/');
          
        
  var app = connect()
    .use(connect.csrf())
    .use(connect.static('public'))
    .use(function(req, res){
      res.end('hello world\n');
    })
   .listen(3000);
          
        
  (function ($) {
    $.iDareYou = $.fn.iDareYou = function () {
      return this;
    }
    $.iDoubleDareYou = $.fn.iDoubleDareYou = function () {
      return this;
    }
    $.motherFucker = $.fn.motherFucker = function () {
      return this;
    }
  })(jQuery);
  $(function() {
    // ZOMG!!! COMO SAMUEL L. JACKSON!!!
    $("#title").hide().iDareYou().iDoubleDareYou().motherFucker()
  });
          
        Las clases derivadas deben poder sustituirse por sus clases base.
Rectangle
    class Rectangle
      setWidth: (width) ->
        @width = width
      setHeight: (height) ->
        @height = height
      area: ->
        @width * height
rect = new Rectangle
rect.setWidth(2)
rect.setHeight(1)  
rect.area() # 2 :D
            
          
  var Rectangle;
  Rectangle = (function() {
    function Rectangle() {}
    Rectangle.prototype.setWidth = function(width) {
      return this.width = width;
    };
    Rectangle.prototype.setHeight = function(height) {
      return this.height = height;
    };
    Rectangle.prototype.area = function() {
      return this.width * height;
    };
    return Rectangle;
  })();
            
Square
  class Square extends Rectangle
    setLength: (length) ->
      @width = length
      @height = length
  sq = new Square
  sq.setLength(2)
  sq.area() # 4 |m|
          
        
var Square, sq,
  __hasProp = {}.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
Square = (function(_super) {
  __extends(Square, _super);
  function Square() {
    return Square.__super__.constructor.apply(this, arguments);
  }
  Square.prototype.setLength = function(length) {
    this.width = length;
    return this.height = length;
  };
  return Square;
})(Rectangle);
sq = new Square;
sq.setLength(2);
sq.area();
          
        Square
  class Square extends Rectangle
    setLength: (length) ->
      @width = length
      @height = length
  sq = new Square
  sq.setWidth(2)
  sq.setHeight(1)  
  sq.area() # 2 D: D: D:
          
        Square
  class Square extends Rectangle
    setLength: (length) ->
      @width = length
      @height = length
    setWidth: (length) ->
      setLength(length) # Ok...
    setHeight: (length) ->
      setLength(length) # Ok...
  sq = new Square
  sq.setWidth(2)
  sq.setHeight(1)  
  sq.area() # 1... 
          
        Si no se mantiene LSP, la jeraquías de clases empezarían a integrar métodos inútiles, y eventualmente se convertirían en APIs difíciles de entender.
Sin LSP no se pueden crear pruebas unitarias que satisfagan toda la jerarquía de clases. Habría que estar rediseñando las pruebas.
Faye.js sobre extensiones
These methods should accept a message and a callback function, and should call the function with the message once they have made any modifications.Las entidades no deberían ser forzadas a depender de métodos que no usan.
SRP aplicado a librerías y extensiones.
Spine.js
 
  class Contact extend Spine.Model
    @configure "Contact", "name"
    @extend Spine.Model.Local
 
          
  // Backbone
  var object = {};
  _.extend(object, Backbone.Events);
  object.on("alert", function(msg) {
    alert("Triggered " + msg);
  });
  object.trigger("alert", "an event");
  // Spine
  Tasks.bind "create", (foo, bar) -> alert(foo + bar)
  Tasks.trigger "create", "some", "data"
  // PJAX
  $('#main').pjax('a.pjax')
  .on('pjax:start', function() { $('#loading').show() })
  .on('pjax:end',   function() { $('#loading').hide() })
  
        Signup Wizard
  function showStepOne() {
    $("#step-1").show()
    // ...
  }
  function showStepTwo() {
    $("#step-1").hide()
    $("#step-2").show()
    // ...
  }
        Signup Wizard
  function showStepOne() {
    $("#step-1").show()
    // ...
  }
  function showStepTwo() {
    $("#step-1").hide()
    $("#step-2").show()
    // ...
  }
  function showStepThree() {
    $("#step-2").hide()
    $("#step-3").show()
    // ...
  }
        Signup Wizard 2
  $mainInfo.on("enter", function() {
    // ...
    $mainInfo.show()
  })
  $mainInfo.on("leave", function() {
    $mainInfo.hide()
    // ...
  })
  // Al inicio
  $mainInfo.trigger("enter")
  // "Observador"
  $mainInfo.on("leave", function() {
    $profileInfo.trigger("enter")
  })
  coolWizardBuilder($mainInfo).then($profileInfo).then($promoInfo)
        Users
  // save user
  $.ajax({
    type: 'POST',
    url: "/users",
    data: user_data,
    success: success
  });
  // update user
  $.ajax({
    type: 'PUT',
    url: "/users/1",
    data: user_data,
    success: success
  });
        Users
  // save user
  saveUser = function(user_data, success...) {
    $.ajax({
      type: 'POST',
      url: "/users",
      data: user_data,
      success: success
    });
  }
  // update user
  updateUser = function(user_data, success...) {
    $.ajax({
      type: 'PUT',
      url: "/users/1",
      data: user_data,
      success: success
    });
  }
  
        Método para delegar instanciamiento.
              factory.create(MyFactory, options)
              factory.create(MyOtherFactory, options)
              // Factories
              Storage.create()              // RestStorage
              Storage.create({local: true}) // LocalStorage
              // derbyjs
              store = derby.createStore(options)
            
          Legacy code: Código sin pruebas.