Object Composition in JavaScript

Object Composition in JavaScript

A Bit On OOP

Chances are, if you hear the words “object oriented programming”, this is what you will think of. The tried, true, and boring example of classical inheritance. One class extends another, mostly based around an animal or car of some kind.

class Reptile() {
scales() {
return true;
}

class Snake extends Reptile {
slither() {
return 'slither, slither, slither. hiss';
}
}

let snek = new Snake();
snek.slither();

That’s fine, more than fine even. Examples like this have helped every budding computer science, software engineering, and multimedia students understand the power of objects in a clean and simple way. Yet, too many new developers stop here and don’t delve into other ways of using objects to bring functionality to their projects. Modules patterns, decorator patterns, multiple inheritance, prototypes, and objects composition are all valid concepts, and in some instances make much more sense than regular class extension. For JavaScript, a language that was without classes for well over a decade, this is especially true. For now though, let’s start with object composition, what it is, and why we would want to use it.

So, What Is Object Composition.

Put bluntly, object composition is the coming together of several tiny objects to form a whole under a class that acts as a kind of orchestrator. Take a bicycle for example. A bicycle is made up of many tiny and intricate parts that all have to work together to achieve a single goal, move the rider forward. Some of these parts might include…

Now, class extension is great, but only solves a very specific set of problems. Extension is fantastic for creating trees hierarchies of functionality, but it doesn’t really help us with more complex machines. For example, our bicycle’s breaks and wheels may need to be instances of their own classes, and they will need to be able to work together. When you use the breaks, the wheels shouldn’t be allowed to move. Also, forcing the wheels to move while breaks are being used may cause damage to the break system. Something needs to manage this relationship.

class Breaks {
constructor() {
this.on = false;
this.damage = 0;
}
}

class Wheels {
turn() {
console.log('move forward');
}
}

class Bicycle {
constructor(options) {
this.wheels = options.wheels;
this.breaks = options.breaks;
}

breakToggle(state) {
this.breaks.on = state;
}

move() {
if(this.breaks.on) {
this.breaks.damage++;
}

if(this.breaks.damage < 3) {
this.wheels.turn();
}
else {
console.log('you broke the breaks!');
}
}
}

let myBike = new Bicycle({
wheels: new Wheels(),
breaks: new Breaks()
});

myBike.move(); // move forward
myBike.breakToggle(true);
myBike.move(); // move forward
myBike.move(); // move forward
myBike.move(); // you broke the breaks!

When And Where?

Low Level APIs

Libraries like D3 and jQuery, or the JS APIs for canvas, DOM manipulation, and audio are prime candidates for object composition. Anything that includes a wide variety of tiny manipulatable objects works well. For a more practical example, see how we can wrap a basic D3 line graph in an ES6 class.

'use strict';

import d3 from 'd3';

class Graph {
constructor(options) {
this.xScale = options.xScale;
this.yScale = options.yScale;
this.line = options.line;
this.data = options.data;
this.width = options.width;
this.height = options.height;
}

path() {
return this
.line
.x(function(d) { return this.xScale(d.x); })
.y(function(d) { return this.yScale(d.y); })
.interpolate('linear');
}

render() {
return `<svg width=${this.width}
height=
${this.height}>
<path d=
${this.path()(this.data)}/>
</svg>`
;
}
}

export default Graph;

Because object composition works best with complex mechanisms, it works great with graph libraries like D3. You really do need something to manage all of the objects built around scales, ranges, domains, axis, data, and more. We will make use of a basic ES6 class. Though this is a very very basic example, you can see how our render and path functions don’t really have any complex logic of their own. They just tell the other objects how to do their jobs.

You might be thinking, “this sounds like a controller”. And you would be partially right. Controllers like web components operate really well under this pattern.

React

When thinking about how to implement your composed objects, you have many options. Though, the best candidate I have seen to date is React, due to how little code it takes to go from plain ES6 classes to working web components. Let’s revisit the previous ES6 only example and see what it would look like as a React Component.

import d3 from 'd3';
import React from 'react';

class Graph extends React.Component {
constructor(props) {
super(props);
}

path() {
return this
.props
.line
.x(function(d) { return this.props.xScale(d.x); })
.y(function(d) { return this.props.yScale(d.y); })
.interpolate('linear');
}

render() {
return (
<svg width={ this.props.width }
height={ this.props.height }>
<path d={ this.path()(this.props.data) }/>
</svg>
);
}
}

Conclusion

Object Composition may sound scary, or boring, or possibly both to you. Yet, it’s something you all have probably been doing in JavaScript for as long as you’ve been developing web applications. So why did we learn all of this? Well, there are times in development when it’s important to put a name to a concept and fully understand it. These moments help us look at new frameworks and libraries and not get immediately confused. They help us make conscious decisions about our development and prevent us from falling into hidden traps full of snakes and other venomous things.

With the latest standardization of object oriented tooling in JavaScript, it’s more important than ever to understand these patterns. Too often do we need to learn new libraries and frameworks and break neck speeds, and anything that may be able to help us do that is a life saver that your clients, your boss, or your teammates will thank you for. Though learning the tough lessons the hard way are memorable, they also come at a price. Let’s all learn to be more mindful of our decisions and build something awesome.

Happy Coding.