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
170
Waste in Software Development
raganwald
0
220
First-Class Commands, the 2017 Edition
raganwald
1
250
Optimism and the Growth Mindset
raganwald
0
360
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
Heart of Swift Concurrency
koher
0
1k
MCP Appsでつくるチャット駆動な地図アプリ
nakamori1024
0
110
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
260
大喜利で理解するLLM as a Judge / Understanding LLM-as-a-Judge through Ogiri
rockname
0
170
選挙速報を多くのユーザーへ 届ける Live Activities 設計
hamayokokuririn
0
180
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
320
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
160
フロントエンドUIフレームワークのこれまでとこれから
ssssota
5
3k
すこし踏み込む CancellationToken
htkym
2
1.3k
一人だけ、Kiroが静止する日
hideg
0
130
JPUG勉強会 OSSデータベースの内部構造を理解しよう(第2回)
oga5
0
280
20260914 AIエージェント時代のPlatform Engineering LLM基盤とプロダクトの責務境界線
kanfab1
7
2.2k
Featured
See All Featured
KATA
mclloyd
PRO
35
16k
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
340
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Statistics for Hackers
jakevdp
799
230k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Chasing Engaging Ingredients in Design
codingconduct
0
340
The Language of Interfaces
destraynor
162
27k
How to build a perfect <img>
jonoalderson
1
6k
30 Presentation Tips
portentint
PRO
1
410
AI: The stuff that nobody shows you
jnunemaker
PRO
10
1.1k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.7k
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
12k
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