Many times we encounter situations where our code depends on a complicated data structure or a data structure that has implicit semantics. For example, let’s say we create a chart and the data that we have to use is a response from a server (we can’t change the structure of the response). The response from the server looks like this:
[
["2018-12-02", 1000],
["2018-11-02", 900],
["2018-10-02", 200],
["2018-09-02", 300]
]
This structure has an implicit meaning, it is an array of arrays, each of which has two values, the first represents a date and the second one a value. The naive approach is to use this structure directly in our code, like:
class Chart {
constructor(data){
this.data = data;
}
createDomains(){
x.domain(d3.extent(this.data, d => d[0])); // date
y.domain(d3.extent(this.data, d => d[1])); // value
}
createLine(){
d3.line()
.x(d => d[0]) // date
.y(d => d[1]); // value
}
formatDate(){
this.data.forEach(d => {
d[0] = parseTime(d[0]); // date
});
}
}
The problem with this approach is that, if the server decides to change the order of the values, or introduces a new one in the first or second place of the array, our code we break, let’s say the structure changes to look like:
[
["Dinos", "2018-12-02", 1000],
["Nikos", "2018-11-02", 900],
["Petros", "2018-10-02", 200],
["Giannis", "2018-09-02", 300]
]
Now our class is broken, every single method of it is broken. We have to update every method to fix the problem. A better approach would be to not depend on the structure of the response.
class Chart {
constructor(data){
this.data = data.map(dataPoint => ({
date: dataPoint[0],
value: dataPoint[1],
}));
}
createDomains(){
x.domain(d3.extent(this.data, d => d.date));
y.domain(d3.extent(this.data, d => d.value));
}
createLine(){
d3.line()
.x(d => d.date)
.y(d => d.value)
.values(this.data)
}
formatDate(){
this.data.forEach(d => {
d.date = parseTime(d.date);
});
}
}
If the server decides to change the passed data structure, we will have to update our code only in a single place, the constructor, leaving every other method untouched.
Top comments (3)
@Proxy in JS is great for exactly this. Using a generic proxy decorator, you can create custom proxy classes to instantiate the objects you need from the responses you get.
Needs a polyfill though.
Yep. polyfill.io ? never used it but certainly seems cool.