Un design pattern è soluzione generale e riusabile ad un problema ricorrente; ma tutti i design patterns "classici" possono essere utilizzati in Javascript? Esistono design patterns tipici di Javascript? In questo talk vedremo quali design pattern classici si possono implementare in Javascript, e come, così come nuovi pattern possono sfruttare al massimo le caratteristiche del linguaggio.
22. Always declare variables with var
function sum(x, y){
var result = x + y;
return result;
}
{pattern}
Wednesday, November 10, 2010
23. function foo(){
var a = b = 0;
//...
}
{antipattern}
Wednesday, November 10, 2010
24. b become global
function foo(){
var a = (b = 0);
//...
}
{antipattern}
Wednesday, November 10, 2010
25. don’t use assign chain in definition
function foo(){
var a, b;
a = b = 0;
//...
}
{pattern}
Wednesday, November 10, 2010
26. Single var pattern
function func(){
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body...
}
{pattern}
Wednesday, November 10, 2010
29. Hoisting
myname = "global"; // global variable
function func(){
// code...
console.log(myname); // "undefined"
// code...
var myname = "local";
console.log(myname); // "local"
}
func();
{antipattern}
Wednesday, November 10, 2010
30. Hoisting
myname = "global"; // global variable
function func(){
var myname = "declared";
// code...
console.log(myname); // "declared"
// code...
myname = "local";
console.log(myname); // "local"
}
func();
{pattern}
Wednesday, November 10, 2010
31. Against minimum vertical distance
principle
“Variables should be declared as close to
their usage as possible”
Robert C. Martin - Clean Code
Wednesday, November 10, 2010
32. Essential
Literal and Constructor
Wednesday, November 10, 2010
34. It’s easy...
var person = new Object();
person.name = "Scott";
person.say = function(){
return "I am " + this.name;
};
console.log(person.say());
Wednesday, November 10, 2010
35. but wrong! :-(
var person = new Object();
person.name = "Scott";
person.say = function(){
return "I am " + this.name;
};
console.log(person.say());
{antipattern}
Wednesday, November 10, 2010
36. var person = {};
person.name = "Scott";
person.say = function(){
return "I am " + this.name;
};
console.log(person.say());
{pattern}
Wednesday, November 10, 2010
37. What if we need similar objects...
var person = {};
person.name = "Scott";
person.say = function(){
return "I am " + this.name;
};
console.log(person.say()); // I am Scott
var otherPerson = {};
otherPerson.name = "Tiger";
otherPerson.say = function(){
return "I am " + this.name;
};
console.log(otherPerson.say()); // I am Tiger
Wednesday, November 10, 2010
38. A lot of duplication
var person = {};
person.name = "Scott";
person.say = function(){
return "I am " + this.name;
};
console.log(person.say()); // I am Scott
var otherPerson = {};
otherPerson.name = "Tiger";
otherPerson.say = function(){
return "I am " + this.name;
};
console.log(otherPerson.say()); // I am Tiger
Wednesday, November 10, 2010
40. Custom Constructor Functions
var Person = function(name){
this.name = name;
this.say = function(){
return "I am " + this.name;
}
}
var person = new Person("Scott");
console.log(person.say()); // I am Scott
{pattern}
Wednesday, November 10, 2010
41. Behind the scenes...
var Person = function(name){
// var this = {};
this.name = name;
this.say = function(){
return "I am " + this.name;
};
// return this;
};
{pattern}
Wednesday, November 10, 2010
42. So, at the end...
var Person = function(name){
this.name = name;
this.say = function(){
return "I am " + this.name;
};
};
var scott = new Person('Scott');
var tiger = new Person('Tiger');
console.log(scott.say());
console.log(tiger.say());
{pattern}
Wednesday, November 10, 2010
43. What if we forget new?
Wednesday, November 10, 2010
44. this will point to global object
var Person = function(name){
this.name = name;
this.say = function(){
return "I am " + this.name;
};
};
var scott = new Person('Scott')
var adam = Person('Adam')
console.log(typeof scott); //object
console.log(scott.name); // Scott
console.log(typeof adam); //'undefined'
console.log(window.name); // Adam
Wednesday, November 10, 2010
46. var Person = function(name){
var that = {};
that.name = name;
that.say = function(){
return "I am " + that.name;};
return that;
};
var scott = new Person('Scott')
var adam = Person('Adam')
console.log(typeof scott); //Object
console.log(scott.name); // Scott
console.log(typeof adam); //Object
console.log(adam.name); // Adam
{pattern}
Wednesday, November 10, 2010
47. Drawback: we loose prototype reference :-(
var Person = function(name){
var that = {};
that.name = name;
that.say = function(){
return "I am " + that.name;
};
return that;
};
Person.prototype.iamhumanbeing = true;
var scott = new Person('Scott')
var adam = Person('Adam')
console.log(scott.iamhumanbeing); // undefined
console.log(adam.iamhumanbeing); // undefined
Wednesday, November 10, 2010
48. Interm!zo
Prototype property
Wednesday, November 10, 2010
49. Define ancestors chain
var foo = {one: 1, two: 2};
var bar = {three: 3};
foo.__proto__ = bar;
console.log(foo.one);
console.log(foo.three);
Wednesday, November 10, 2010
50. bar
three: 3
foo
one: 1
two: 2
__proto__
Wednesday, November 10, 2010
51. Behind the scenes...
var Person = function(name){
// this.prototype = {constructor: this}
var that = {};
that.name = name;
that.say = function(){
return "I am " + that.name;
};
return that;
};
Wednesday, November 10, 2010
52. Self invoking constructor
var Person = function(name){
if (this instanceof Person) {
this.name = name;
this.say = function(){
return "I am " + that.name;
}
}
else {
return new Person(name);
}
};
Person.prototype.iamhumanbeing = true;
var scott = new Person('Scott')
var adam = Person('Adam')
console.log(scott.name); // Scott
console.log(adam.name); // Adam
console.log(scott.iamhumanbeing); // true
console.log(adam.iamhumanbeing); // true
{pattern}
Wednesday, November 10, 2010
53. Essential
Functions
Wednesday, November 10, 2010
61. function Gadget(){
this.name = 'iPod';
this.stretch = function(){
return 'iPad';
}
};
var toy = new Gadget();
console.log(toy.name); // `iPod`
toy.name = 'Zune'
console.log(toy.name); // `Zune` is public
console.log(toy.stretch()); // stretch() is public
{antipattern}
Wednesday, November 10, 2010
62. Create private member
function Gadget(){
var name = 'iPod';
this.getName = function(){
return name;
}
};
var toy = new Gadget();
console.log(toy.getName()); // `iPod`
toy.name = 'Zune'
console.log(toy.getName()); // `iPod`
{pattern}
Wednesday, November 10, 2010
63. for methods too
function Gadget() {
var name = 'iPod';
var upgrade = function(){
return 'iPhone';
}
this.getName = function () {
return name;
}
this.pay = function() {
return upgrade();
}
};
var toy = new Gadget();
console.log(toy.pay()); // `iPhone`
console.log(toy.upgrade()); // `error`
{pattern}
Wednesday, November 10, 2010
64. Advanced
Code reuse patterns
Wednesday, November 10, 2010
66. Classical inheritance
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
this.name = name;
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
67. function(){
return 'My name is ' + this.name;
};
Parent.prototype
say()
new Parent()
name: Larry
__proto__
console.log(dad.say());
Wednesday, November 10, 2010
68. Default Classical Inheritance pattern
function inherit(C, P) {
C.prototype = new P();
};
Wednesday, November 10, 2010
69. function(){
return 'My name is ' + this.name;
};
Parent.prototype
say()
new Parent()
new Child() name: Larry
name: Scott __proto__
__proto__
console.log(kid.say());
Wednesday, November 10, 2010
70. Drawback: it doesn’t call parent constructor
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
this.name = name;
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
71. Drawback: it doesn’t call parent constructor
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){ };
function inherit(C, P) {
C.prototype = new P();
};
inherit(Child, Parent);
var kid = new Child('Scott');
console.log(kid.say()); // 'My name is undefined'
Wednesday, November 10, 2010
72. Pattern Extension: rent a constructor
function Child(name){
Parent.apply(this, arguments);
};
Wednesday, November 10, 2010
73. Pattern Extension: rent a constructor
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
Parent.apply(this, arguments);
};
function inherit(C, P){
C.prototype = new P();
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
74. Drawback: parent constructor is called twice
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
Parent.apply(this, arguments);
};
function inherit(C, P){
C.prototype = new P();
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
75. Drawback: parent constructor is called twice
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
Parent.apply(this, arguments);
};
function inherit(C, P){
C.prototype = new P();
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
76. Drawback: parent constructor is called twice
function Parent(name){
this.name = name;
};
Parent.prototype.say = function(){
return 'My name is ' + this.name;
};
function Child(name){
Parent.apply(this, arguments);
};
function inherit(C, P){
C.prototype = new P();
};
inherit(Child, Parent);
var dad = new Parent('Larry');
var kid = new Child('Scott');
console.log(dad.say()); // 'My name is Larry'
console.log(kid.say()); // 'My name is Scott'
Wednesday, November 10, 2010
77. mmmm let’s try with the same prototype
Wednesday, November 10, 2010
78. Share the same prototype
function inherit(C, P){
C.prototype = P.prototype;
};
Wednesday, November 10, 2010
79. Parent.prototype
say()
new Child() new Parent()
name: Scott name: Larry
__proto__ __proto__
Wednesday, November 10, 2010
80. Share the same prototype
Inheritance works as expected
Constructor called only once
Low memory footprint
Wednesday, November 10, 2010
81. Share the same prototype
Child objects can affect other objects
Wednesday, November 10, 2010
82. Enhance the pattern: temporary constructor
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
};
Wednesday, November 10, 2010
83. Parent.prototype
say()
new Parent() new F()
name: Larry __proto__
__proto__
new Child()
name: Scott
__proto__
Wednesday, November 10, 2010
84. The Holy Grail Pattern of classical inheritance
function inherit(C, P) {
var F = function () {};
F.prototype = P.prototype;
C.prototype = new F();
C.uber = P.prototype;
C.prototype.constructor = C;
};
Wednesday, November 10, 2010
88. What we want in prototypal inheritance
var parent = {
name: "Larry",
say: function(){
return "My name is " + this.name;
}
};
var child = object(parent);
child.name = 'Scott'
console.log(child.say()); // "Scott"
Wednesday, November 10, 2010
89. Prototypal inheritance function
function object(o) {
function F() {}
F.prototype = o;
return new F();
};
Wednesday, November 10, 2010
90. parent
name: Scott
say()
child = new F()
name: Larry
__proto__
Wednesday, November 10, 2010
91. With constructor function
var Parent = function(name){
this.name = name;
this.say = function(){
return "My name is " + this.name;
}
};
var child = object(new Parent("Larry"));
child.name = 'Scott'
console.log(child.say()); // "Scott"
Wednesday, November 10, 2010
94. Goals of inheritance is reuse and reduce duplication
Wednesday, November 10, 2010
95. isA relationship...
Liskov principle...
difficult to tame inheritance...
Wednesday, November 10, 2010
96. A modern and better approach is to use
Mix-In
Wednesday, November 10, 2010
97. A beahviour...
var Serializer = function () {};
Serializer.prototype = {
serialize: function () {
var output = [];
for (key in this) {
// append this[key] to output
// ...
}
return output.join(', ');
}
};
Wednesday, November 10, 2010
98. another beahviour...
var XmlBuilder = function () {};
XmlBuilder.prototype = {
toXml: function () {
var output = '';
for (key in this) {
// append xml of this[key] to output
// ...
}
return output;
}
};
Wednesday, November 10, 2010
99. and an object...
var Author = function (name, books) {
this.name = name || "";
this.books = books || [];
}
Wednesday, November 10, 2010
100. result!
augment(Author, Serializer);
augment(Author, XmlBuilder);
var author = new Author('Umberto Eco',
['Il nome della rosa',
'Il Pendolo di Foucault']);
var serializedString = author.serialize();
console.log(serializedString); // name: Umberto Eco,
// books: Il nome della rosa,
// Il Pendolo di Foucault
var xmlString = author.toXml();
console.log(xmlString); //<name>Umberto Eco</name>
// <book>Il nome della rosa</book>
// <book>Il Pendolo di Focault</book>
Wednesday, November 10, 2010
101. The recipe
function augment(receivingClass, givingClass) {
for (methodName in givingClass.prototype) {
if (!receivingClass.prototype[methodName]) {
receivingClass.prototype[methodName] =
givingClass.prototype[methodName];
}
}
}
Wednesday, November 10, 2010
102. Advanced
Design Patterns
Wednesday, November 10, 2010
106. Factory pattern
creation of objects
subclasses decide which class to instantiate
Wednesday, November 10, 2010
107. var BicycleFactory = {
createBicycle: function(model){
var bicycle;
switch (model) {
case 'The Speedster':
bicycle = new Speedster();
break;
case 'The Lowrider':
bicycle = new Lowrider();
break;
case 'The Comfort Cruiser':
default:
bicycle = new ComfortCruiser();
}
Interface.ensureImplements(bicycle, Bicycle);
bicycle.assemble();
bicycle.wash();
return bicycle;
}
};
var californiaCruisers = new BicycleFactory();
var yourNewBike = californiaCruisers.createBicycle('The Speedster');
Wednesday, November 10, 2010
108. A more concrete example...
var XMLHttpFactory = function(){
this.createXMLHttp = function(){
if (typeof XMLHttpRequest !== "undefined") {
return XMLHttpRequest();
}
else
if (typeof window.ActiveXObject !== "undefined") {
return ActiveXObject("MSXML2.XMLHttp");
}
else {
alert("XHR Object not in production");
}
}
};
var xhr = new XMLHttpFactory().createXMLHttp();
Wednesday, November 10, 2010
112. Extract condition and action....
var XhrStandard = function(){
this.canHandle = function(){
return typeof XMLHttpRequest !== "undefined";
}
this.xhr = function(){
return XMLHttpRequest();
}
};
Wednesday, November 10, 2010
113. another condition and action....
var XhrIe = function(){
this.canHandle = function(){
return typeof ActiveXObject !== "undefined";
}
this.xhr = function(){
return ActiveXObject("MSXML2.XMLHttp");
}
};
Wednesday, November 10, 2010
114. and last one condition and action....
var XhrError = function(){
this.canHandle = function(){
return true;
}
this.xhr = function(){
throw("XHR Object not in production");
}
};
Wednesday, November 10, 2010
115. and the engine!
var XMLHttpFactory = function(){
//... ChainLinks...
var creators = [new XhrStandard(),
new XhrIe(),
new XhrError()];
this.createXMLHttp = function(){
var creator;
for(var i = 0; i < creators.length; ++i){
creator = creators[i];
if(creator.canHandle()) {
return creator.xhr();
}
}
}
};
var xhr = new XMLHttpFactory().createXMLHttp();
console.log(xhr);
Wednesday, November 10, 2010
119. last action...
var XhrError = function(){
this.canHandle = function(){
return true;
}
this.xhr = function(){
throw ("XHR Object not in production");
}
this.addSuccessor = function(successor){
this.successor = successor;
return this.successor;
}
};
Wednesday, November 10, 2010
120. and the engine!
var XMLHttpFactory = function(){
//... ChainLinks...
var creator = (function(){
var head = new XhrIe();
head.addSuccessor(new XhrStandard())
.addSuccessor(new XhrError());
return head;
})();
this.createXMLHttp = function(){
return creator.xhr();
}
};
var xhr = new XMLHttpFactory().createXMLHttp();
console.log(xhr);
Wednesday, November 10, 2010
126. Extracted same and similar behaviours....
var ChainLink = function() {};
ChainLink.prototype.exec = function(){
if(this.canHandle()) {
return this.doIt();
}
return this.successor.exec();
};
ChainLink.prototype.addSuccessor = function(successor){
this.successor = successor;
return this.successor;
}
Wednesday, November 10, 2010
127. Augment an action...
var XhrStandard = augment(function(){
this.canHandle = function(){
return typeof XMLHttpRequest !== "undefined";
}
this.doIt = function(){
return XMLHttpRequest();
};
}, ChainLink);
Wednesday, November 10, 2010
128. another action...
var XhrIe = augment(function(){
this.canHandle = function(){
return typeof ActiveXObject !== "undefined";
}
this.doIt = function(){
return this.doIt();
};
}, ChainLink);
Wednesday, November 10, 2010
129. and last one.
var XhrError = augment(function(){
this.canHandle = function(){
return true;
}
this.doIt = function(){
throw("XHR Object not in production");
}
},ChainLink);
Wednesday, November 10, 2010
130. and the engine is the same!
var XMLHttpFactory = function(){
//... ChainLinks...
var creator = (function(){
var head = new XhrIe();
head.addSuccessor(new XhrStandard())
.addSuccessor(new XhrError());
return head;
})();
this.createXMLHttp = function(){
return creator.xhr();
}
};
var xhr = new XMLHttpFactory().createXMLHttp();
console.log(xhr);
Wednesday, November 10, 2010
131. It’s just a beginning...
Wednesday, November 10, 2010