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
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
150
Waste in Software Development
raganwald
0
210
First-Class Commands, the 2017 Edition
raganwald
1
240
Optimism and the Growth Mindset
raganwald
0
340
ember-concurrency: an experience report
raganwald
1
170
Optimism II
raganwald
0
440
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
SLOをサービス品質の共通言語にするために 取り組んできたこと
wakana0222
0
410
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
180
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.7k
SREの積み重ねがAI駆動開発のガードレールになった ― 7つの実践/SRE Guardrails The 7
tomoyakitaura
7
3.1k
Snowflake Summitでの新機能 CoCo / CoWork / snowflake-summit-2026-overall-what-new-coco
tatsuhiro
1
220
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.6k
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
830
どこまでゆるくて許されるのか
tk3fftk
0
430
ADKを使って簡単にAIエージェントを作ってみよう
k1mu21
0
290
吝嗇家のためのAI活用 / AI development for miser - ChatGPT + Issue Driven Development
tooppoo
0
180
ランチタイムLT会3周年!ランチタイムLT会を3年間続けられたお話
y0hgi
1
130
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
240
Featured
See All Featured
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4.1k
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
390
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
170
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
320
Scaling GitHub
holman
464
140k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
300
Design in an AI World
tapps
1
260
We Have a Design System, Now What?
morganepeng
55
8.2k
Writing Fast Ruby
sferik
630
63k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.5k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.2k
Color Theory Basics | Prateek | Gurzu
gurzu
0
380
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