Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Duck Typing, Compatibility, and the Adaptor Pat...
Search
Reg Braithwaite
September 12, 2014
Programming
11k
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Duck Typing, Compatibility, and the Adaptor Pattern
Nordic JS, 2014
Reg Braithwaite
September 12, 2014
More Decks by Reg Braithwaite
See All by Reg Braithwaite
Courage
raganwald
0
160
Waste in Software Development
raganwald
0
220
First-Class Commands, the 2017 Edition
raganwald
1
250
Optimism and the Growth Mindset
raganwald
0
350
ember-concurrency: an experience report
raganwald
1
170
Optimism II
raganwald
0
450
Optimism
raganwald
0
2.1k
First-Class Commands: an unexpectedly fertile design pattern
raganwald
4
3k
JavaScript Combinators, the “six” edition
raganwald
8
1.5k
Other Decks in Programming
See All in Programming
標準パッケージに uuid が追加された 背景から見る Go らしい意思決定 / go_127_uuid_decision
convto
5
5.7k
GKE で Pod の見方を変えたら、スケールアウト時の挙動を真に捉えられた話
stkk
0
110
個人開発基盤をまるごとCloudflareに引っ越して爆速で総合的体験を向上させた話
tinykitten
0
140
iOS開発×AI駆動開発 〜最近使って便利だったスキルの話〜
nogu66
0
140
AIとGame Jamで、ゲームを完成させた話
takahirosaeki
0
120
新卒PdEのリアル
ryu1013
1
480
Building an Out-of-Order CPU
latte72
1
750
Seeing Through Serverless: Observability for AWS Lambda with ADOT and CloudWatch Application Signals
seike460
PRO
1
130
思考垂れ流し開発 ~音声入力 × AIエージェント × 開発ハーネスによる試行錯誤~
npostring
0
1.1k
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
190
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
1
490
スマート反転とウェブアクセシビリティ
camiha
0
180
Featured
See All Featured
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.6k
How STYLIGHT went responsive
nonsquared
100
6.3k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
500
Documentation Writing (for coders)
carmenintech
77
5.5k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
270
Visual Storytelling: How to be a Superhuman Communicator
reverentgeek
2
660
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
590
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
930
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
1.3k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
450
Amusing Abliteration
ianozsvald
1
290
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