Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Duck Typing, Compatibility, and the Adaptor Pat...
Search
Reg Braithwaite
September 12, 2014
Programming
7
10k
Duck Typing, Compatibility, and the Adaptor Pattern
Nordic JS, 2014
Reg Braithwaite
September 12, 2014
Tweet
Share
More Decks by Reg Braithwaite
See All by Reg Braithwaite
Courage
raganwald
0
120
Waste in Software Development
raganwald
0
190
First-Class Commands, the 2017 Edition
raganwald
1
180
Optimism and the Growth Mindset
raganwald
0
300
ember-concurrency: an experience report
raganwald
1
140
Optimism II
raganwald
0
410
Optimism
raganwald
0
2k
First-Class Commands: an unexpectedly fertile design pattern
raganwald
4
2.9k
JavaScript Combinators, the “six” edition
raganwald
8
1.4k
Other Decks in Programming
See All in Programming
エンジニアインターン「Treasure」とHonoの2年、そして未来へ / Our Journey with Hono Two Years at Treasure and Beyond
carta_engineering
0
430
TransformerからMCPまで(現代AIを理解するための羅針盤)
mickey_kubo
7
5.4k
CSC305 Lecture 08
javiergs
PRO
0
280
Go言語の特性を活かした公式MCP SDKの設計
hond0413
2
550
GC25 Recap: The Code You Reviewed is Not the Code You Built / #newt_gophercon_tour
mazrean
0
110
SODA - FACT BOOK(JP)
sodainc
1
8.8k
CSC509 Lecture 07
javiergs
PRO
0
250
Domain-centric? Why Hexagonal, Onion, and Clean Architecture Are Answers to the Wrong Question
olivergierke
3
980
Android16 Migration Stories ~Building a Pattern for Android OS upgrades~
reoandroider
0
140
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
490
Software Architecture
hschwentner
6
2.3k
AI 駆動開発におけるコミュニティと AWS CDK の価値
konokenj
5
250
Featured
See All Featured
Code Reviewing Like a Champion
maltzj
526
40k
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Music & Morning Musume
bryan
46
6.9k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
34
2.3k
Testing 201, or: Great Expectations
jmmastey
45
7.7k
The Language of Interfaces
destraynor
162
25k
Rails Girls Zürich Keynote
gr2m
95
14k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.7k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
31
2.7k
Mobile First: as difficult as doing things right
swwweet
225
10k
YesSQL, Process and Tooling at Scale
rocio
173
15k
Transcript
None
the$art$of$the$javascript$metaobject$protocol Duck%Typing,%Compa1bility,%and The$Adaptor$Pa-ern
None
Duck%typing%is%a%style%of%typing%in% which%an%object's%methods%and% proper:es%determine%the%valid% seman:cs,
...rather'than'its'inheritance'from'a' par0cular'class'or'implementa0on'of' an'explicit'interface.
None
Tastes&Like&Fruit
None
Also%Tastes%Like%Fruit
"Write'code'that'depends'upon' tastes1like(fruit)'rather'than' depending'upon'is1a(fruit)."
None
duck%typing%is%a Compa&bility,Technique
None
A"Problem:
None
Conway's)Game)of)Life
"Conway's*Game*of*Life*is*a*two2 state*cellular*automata."
An#Implementa+on 1. Cells,
An#Implementa+on 1. Cells, 2. Universe,
An#Implementa+on 1. Cells, 2. Universe,.and 3. View.
Cell 1. Neighbours,
Cell 1. Neighbours,.and 2. State.
function StandardCell () { this._neighbours = []; this._alive = false;
}
StandardCell.prototype.neighbours = function neighbours (neighbours) { return this._neighbours; }; StandardCell.prototype.setNeighbours
= function setNeighbours (neighbours) { this._neighbours = neighbours.slice(0); return this; };
StandardCell.prototype.alive = function alive () { return this._alive; }; StandardCell.prototype.setAlive
= function setAlive (alive) { this._alive = alive; return this; };
None
moving'through Time
Cell 1. Neighbours, 2. State,2and 3. Transforma9on2of2State.
Universe 1. Neighbourhoods.(not.shown),
Universe 1. Neighbourhoods.(not.shown),.and 2. Transforma:on.of.States.
StandardCell.prototype.nextAlive = function nextAlive () { var alives = this._neighbours.filter(function
(n) { return n.alive(); }).length; if (this.alive()) { return alives === 2 || alives == 3; } else { return alives == 3; } };
Universe.prototype.iterate = function iterate () { var aliveInNextGeneration = this.cells().map(
function (c) { return [c, c.nextAlive()]; } ); aliveInNextGeneration.forEach(function (a) { var cell = a[0], next = a[1]; cell.setAlive(next); }); };
None
drawing Life
View 1. Drawing*a*Cell:
View.prototype.drawCell = function drawCell (cell, x, y) { var xPlus
= x + this.cellSize(), yPlus = y + this.cellSize() this._canvasContext.clearRect(x, y, xPlus, yPlus); this._canvasContext.fillStyle = this.cellColour(cell); this._canvasContext.fillRect(x, y, xPlus, yPlus); return self; };
View.prototype.cellColour = function cellColour (cell) { return cell.alive() ? WHITE
: BLACK; };
None
None
Ch#ch#ch#changes!
None
redesigning(for Colour
function ColourCell () { this._neighbours = []; this._age = 0;
} ColourCell.prototype.neighbours = StandardCell.prototype.neighbours; ColourCell.prototype.setNeighbours = StandardCell.prototype.setNeighbours;
ColourCell.prototype.age = function age () { return this._age; }; ColourCell.prototype.setAge
= function setAge (age) { this._age = age; return this; };
None
moving'through',me in#Colour
ColourCell.prototype.nextAge = function next () { var alives = this._neighbours.filter(function
(n) { return n.age() > 0; }).length; if (this.age() > 0) { return (alives === 2 || alives == 3) ? (this.age() + 1) : 0; } else { return (alives == 3) ? (this.age() + 1) : 0; } };
Universe.prototype.iterate = function iterate () { var ageInNextGeneration = this.cells().map(
function (c) { return [c, c.nextAge()]; } ); ageInNextGeneration.forEach(function (a) { var cell = a[0], next = a[1]; cell.setAge(next); }); };
None
drawing(life in#Colour
var COLOURS = [ BLACK, GREEN, BLUE, YELLOW, WHITE, RED
]; View.prototype.cellColour = function cellColour (cell) { return COLORS[ (cell.age() >= COLOURS.length) ? (COLOURS.length - 1) : cell.age() ]; }; // ...
None
something Doesn't(Fit
Object.keys(StandardCell.prototype) // => [ 'neighbours', 'setNeighbours', 'alive', 'setAlive', 'nextAlive' ]
Object.keys(ColourCell.prototype) // => [ 'neighbours', 'setNeighbours', 'age', 'setAge', 'nextAge' ]
The$state$of$a$cell$is$now$"age"$ instead$of$"aliveness."
None
These%changes%will Ripple&Through&Universe&and&View
our$problem$is$that Universe(is(coupled(to(Cell's( Interface
None
None
how$smart$people Solve&the&Problem
"Make&a&coloured&cell&behave&like&a& standard&cell."
ColourCell.prototype.alive = function alive () { return this._age > 0;
};
ColourCell.prototype.setAlive = function setAlive (alive) { if (alive) { this.setAge(this.age()
+ 1); } else this.setAge(0); return this; };
ColourCell.prototype.nextAlive = StandardCell.prototype.nextAlive;
Object.keys(ColourCell.prototype) // => [ 'neighbours', 'setNeighbours', 'age', 'setAge', 'nextAge', 'alive',
'setAlive', 'nextAlive' ]
Presto!(Backwards(Compa2bility. (it$"tastes$like$a$standard$cell")
None
universe(and(cell(are No#Longer#Coupled
None
At#What#Cost?
None
Increasing*Flexibility Increases(Complexity
None
None
there%is Another(Way
"Make&a&cell&that&behaves&like&a& Standard&Cell,&but&is&implemented&in& terms&of&a&Coloured&Cell."
function AsStandard (colouredCell) { this._colouredCell = colouredCell; } var tastesLikeAStandardCell
= new AsStandard(aColourCell);
AsStandard.prototype.neighbours = function neighbours () { return this._colouredCell.neighbours(); }; AsStandard.prototype.setNeighbours
= function setNeighbours (neighbours) { this._colouredCell.setNeighbours(neighbours); return this; };
AsStandard.prototype.alive = function alive () { return this._colouredCell.age() > 0;
};
AsStandard.prototype.setAlive = function setAlive (alive) { if (alive) { this._colouredCell.setAge(this._colouredCell.age()
+ 1); } else this._colouredCell.setAge(0); return this; };
AsStandard.prototype.nextAlive = function nextAlive () { return this._colouredCell.nextAge() > 0;
}
Object.keys(AsStandard.prototype) // => [ 'neighbours', 'setNeighbours', 'alive', 'setAlive', 'nextAlive' ]
When%we%Upgrade%to%ColourCell 1. View'can'depend'upon'ColourCell,
When%we%Upgrade%to%ColourCell 1. View'can'depend'upon'ColourCell,'and 2. Universe'can'depend'upon'AsStandard.
Sweet! 1. Universe*is*decoupled*from*changes*to*Cell,
Sweet! 1. Universe*is*decoupled*from*changes*to*Cell,*and 2. ColourCell*isn't*burdened*with*backwards*compa>bility.
None
AsStandard)is)an)Adaptor
The$adapter$(sic)$pa/ern$is$a$ so2ware$design$pa/ern$that$allows$ the$interface$of$an$exis8ng$class$to$ be$used$from$another$interface...
It#is#o'en#used#to#make#exis0ng# classes#work#with#others#without# modifying#their#source#code.
TIMTOWTDI
None
good$programmers$borrow$from$smalltalk, Great&Programmers&Steal&from&C++
function colourFromStandard (standard) { return new ColourCell() .setNeighbours(standard.neighbours()) .setAge(standard.alive() ?
1 : 0); }
function standardFromColour (colour) { return new StandardCell() .setNeighbours(colour.neighbours()) .setAlive(colour.age() >
0); }
None
None
Coproxies
None
None
Approaching+the+End
None
adaptors Separate'Concerns
None
and$thus,$adaptors Isolate(Change
None
and$they Decouple(Modules
None
adaptors Solve&Compa+bility&Problems
None
but$they$also$gives$us$the$freedom$to Make%Changes,%Safely
None
when%we're%using%teams%to%build%so3ware%at%scale Adaptors)are)an)Important)Tool
None
None
one$more$thing:
None
None
when%you're%holding%a Version(Shaped.Hammer
None
all#changes#look#like Migra&on)Shaped/Nails
None
Packages(Have(Interlocking( Dependencies
None
Migra&ons*Can*Cascade you$never$know$how$much$work$a$single$upgrade$can$cause
None
migra&on)is)expensive,)so)semver)values Backwards)Compa.bility
None
"duck&typed"&backwards&compa2bility Holds&Progress&Back
None
breaking)compa.bilty Exposes'Clients'to'Bugs
None
adaptors(decouple Compa&bility,from,Progress
None
adaptors(provide Safe%Compa*bility
None
What%would%an%adaptor.aware% package%manager%look%like?
An#Adaptor*Aware#Package#Manager 1. Interfaces,and,Implementa1ons,would,be,independantly, versioned,
An#Adaptor*Aware#Package#Manager 1. Interfaces,and,Implementa1ons,would,be,independantly, versioned, 2. Interfaces,would,have,a,many<to<many,rela1onship,with, Implementa1ons,,mediated,by,Adaptors,
An#Adaptor*Aware#Package#Manager 1. Interfaces,and,Implementa1ons,would,be,independantly, versioned, 2. Interfaces,would,have,a,many<to<many,rela1onship,with, implementa1ons,,mediated,by,adaptors, 3. Clients,would,specifiy,the,required,interface,and, implementa1on,
An#Adaptor*Aware#Package#Manager 1. Interfaces,and,Implementa1ons,would,be,independantly, versioned, 2. Interfaces,would,have,a,many<to<many,rela1onship,with, implementa1ons,,mediated,by,adaptors, 3. Clients,would,specifiy,the,required,interface,and, implementa1on,,and
4. Old,clients,can,benefit,from,bug,fixes,without,rewrites.
None
an#adaptor)aware#package#manager#could Change'the'Way'We'Grow'So0ware
None
and$that$provides Food$for$Thought for$a$very$long$,me$indeed
None
Tack%Så%Mycket!
Reg$Braithwaite,$ GitHub,$Inc. raganwald.com,•,@raganwald Ar#pelag,*Stockholm,* September*19,*2014