What the hell is an object?
EverythingIsAnObject... ?
var iAmAnObject = {};
var soAmI = 5;
var ifYouAreSoAmI = null;
var imAnObject = [];
Definitions in JS
- everything that isn't null/undefined
- empty objects used as key-value stores
- things that have functions in them that reference 'this'
Let's ask Alan Kay
OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.
Alan Kay
So what do Alan Kay's objects look like?
Technical diagram
- messaging/late binding
- hiding of state
Messaging & late-binding
- component sends a message
- another components receives and does... whatever it likes
Results of messaging
- decouples intent from implementation (polymorphism)
- can't see/modify state of receiver
What the hell are objects?
'Objects' are a pattern
- not the specific implementation
Alan Kay's objects are
- entities that accept messages
- that access hidden state
- and do something in response to the message
Everything
var a = 10;
a.toString() // === intToString(a);
Key value data
module.exports = /^hello$/; // uses k/v obj as namespace
var someData = JSON.parse(resp.body); // k/v data
I mean the root
[objects are] like biological cells and/or individual computers on a network, only able to communicate with messages.
The pattern
- yes inheritance sucks, but that's just one implementation
function Account(value) {
return function(command) {
switch(command) {
case "withdraw":
return value -= arguments[1];
case "deposit":
return value += arguments[1];
case "value":
return value;
default:
throw new Error("No such message:" + command);
}
}
}
var account = Account(100)
account("deposit",100); // 200
account("withdraw",50); // 150
account("value"); // 150
So
- a little cell, which has changable state
- which accepts messages into its 'cell wall'
...and hides good ideas from us
What do objects imply?
- EverythingIsAnObject
- we need to track state
- we model things in hierarchies
- we need to hide data
Let's see how often that's true
'Controllers'
- What the hell is a controller?
// locomotive js
var PhotosController = new Controller();
PhotosController.show = function() {
// this._photo is "private", and not available in the view
this.title = this._photo.title;
this.description = this._photo.description;
this.render();
};
PhotosController.destroy = function() {};
PhotosController.resize = function() {};
HTTP
The Hypertext Transfer Protocol (HTTP) is an application-level protocol for distributed, collaborative, hypermedia information systems. It is a generic, stateless, protocol
Stateless
- OO is about state-ful objects
- HTTP is (one of many) stateless protocols
- so using objects to respond to a request is...
Solving its own 'problems'
PhotosController.show = function() {
// this._photo is "private", and not available in the view
Where is the problem?
var ooHtml = new Template({controller: this, template: tpl})
.render();
var html = markdown(template,data);
What the hell is a model?
Typical code in user model
- validation
- checking password
- sessions
- data associated with user
- signup flow
- triggering emails etc etc...
What is the common theme?
'Vaguely relates to a customer'
Modelling fallacy
- it's not a simulation
- our job here is: take HTTP request, load data, turn into HTML string
What's wrong with modelling
- casuses coupling a heap of unrealated concerns
- concepts from auth, domain, presentation...
- we're writing code on computers, not a simulation
Where is its state useful?
Stateless
- we're using HTTP
- we don't keep a user object around between requests
- we load all data per request: from DB, session etc
The 'module as object' pattern
Most 'models' are modules
- just a namespace
- but loads more complex
- now has constructor dependencies
Detecting a hidden module
- unrelated functions
- accessing different fields
- made harder to use as they need initialisation
- some that don't access fields at all
EverythingIsAnObject, state everywhere: not so great
Launching rockets
- code that does things: DB, AJAX etc
- vs code that computes
- OO keeps both in same place
Generic means wonderful abstractions
- works on any container
- map transforms each 'value' inside
- contains say "is there a value like X?"
_.map // [a] -> (a -> b) -> [b]
_.contains // [a] -> a -> Boolean
Let's make it specific
- custom API for something simple
- what has been abstracted?
function User() {}
User.prototype = {
getName: function() {
},
fullName: function() {
}
};
Why do we hide data at all?
Data
- simply taking data and presenting it is stateless: HTTP
- immutability
Mutate === change
- mutate is just jargon for change
- "oh the DNA has mutated" = it changed
Benefits
- if I have the 16th of October, 2007 it stays that way
var someDate = new Date(Date.parse("2007/10/16"));
passDateSomewhere(someDate);
// what happens if...
var timestamp = Date.parse("1995/05/23");
releaseDateOfSomething.setTime(timestamp);
// somewhere else, screaming commences
Wouldn't this be terrible?
- if I have the number 0.30000000000000004
var almostZeroPointThree = 0.1 + 0.2;
passNumber(almostZeroPointThree);
almostZeroPointThree.setValue(104)
// ARGH!
A date does not have state
- a date is a value
- values don't change: e.g Point(x,y), Query(string: x, options: {})
A rule for values
- if you can't meaningfully say "oh which one?" about an instance
- e.g "Oh which 4th of July 1987?"
- it really should be a value
Objects are bad at values
Values for data structures
- we can do the same with structures
Immutable list
function append(headValue,tailQueue) {
var head = {};
Object.defineProperties(head,{
item: { value: headValue, writable: false },
tail: { value: tailQueue, writable: false }
})
return head;
}
function vals(queue) {
var items = [];
while(queue) { items.push(queue.item); queue = queue.tail; }
return items;
}
var a = append;
var q1 = append(2,append(1));
var q2 = append(3,q1);
var q3 = append(4,q1);
console.log(vals(q1),vals(q2),vals(q3)); // [2,1] [3,2,1] [4,2,1]
Use
- we get undo immediately
- can safely share structure without copy
var todos = undefined;
function addTodo(todo) {
return append(todo,todos);
}
function undoAdd(todos) {
return todos.tail;
}
todos = append("buy eggs",todos);
todos = append("buy cheers",todos);
todos = undoAdd(todos);
console.log(todos.item); // "buy eggs"
Instead of _.clone
- try mori
- jargon for the idea: "persistent data structures"
So nothing needs to change?
We still need to store/hear about values
- is user logged in
- tell me when that changes
Events get us quite far
- we're just re-rendering in response to new data
pubSub.on("login:change",renderLoginState);
Streams
- amazing for pure-data (e.g node)
gulp.task('scripts', function() {
return gulp.src(paths.scripts)
.pipe(coffee())
.pipe(uglify())
.pipe(concat('all.min.js'))
.pipe(gulp.dest('build/js'));
});
Also in the browser
- Bacon.js, RxJS
- oh look, map again
var leftTurns = $(".turnLeft").asEventStream("click")
.map(toEvent("turnLeft"));
var rightTurns = $(".turnRight").asEventStream("click")
.map(toEvent("turnRight"));
function toEvent(name) {
return function() {
return {type: name, at: Date.now()};
}
}
var commands = leftTurns.merge(rightTurns);
commands.onValue(applyCommand);
But...
- Still need to know current value
- Any solution?
Properties
Login
Logout
var logins = $(".login").asEventStream("click").map(true);
var logouts = $(".logout").asEventStream("click").map(false);
var loginState = logins.merge(logouts)
.scan(false,function(_,loginState) { return loginState });
function not(x) { return !x }
loginState.assign($(".login"),"prop","disabled");
loginState.map(not).assign($(".logout"),"prop","disabled");
Actually objects are great
When they're used in the right places
Hidden state and large API
- many readers
- contract: can't change state, only read
- painful to do without objects
Other examples
- buffers
-
Property in streams
- shared state of something in time/hardware, not state of stuff we're modelling
I don't think 'objects suck'
I'm just saying they're abused
And they've blinded us to good ideas
Learning derived ideas, not originals with justifications
- Design patterns
- Inheritance
- SOLID
- DCI
- Dependency injection containers
Far from 'best practice', many are painkillers for OO abuse
How to avoid this happening again?
Keep an eye on your ends, not means
- don't be religious
- "Oh no, a helper function" - ItsNotAnObject
Check your assumptions
- don't take things on authority
- try things out for yourself
Today's 'one right way' is tomorrow's mistake
Don't be an X-programmer, be a programmer