Re: Towards Reusable Charts anti-pattern?

622 views
Skip to first unread message

John Firebaugh

unread,
Dec 23, 2012, 1:00:51 PM12/23/12
to d3...@googlegroups.com
I would counter that this is by design: d3 eschews classical
inheritance for more flexible and less brittle alternatives. Here are
alternatives for your examples:

On Sun, Dec 23, 2012 at 8:26 AM, <ben.p....@gmail.com> wrote:
> https://github.com/novus/nvd3/blob/master/src/models/bulletChart.js#L39-L45
> Let's say that I wanted to change the showTooltip method in the first link
> for just one of my charts, my only option is to copy the entire chart to a
> new js file and make my changes. But if this were developed with a classical
> OO pattern I would be able to subclass the chart and override the
> showTooltip method (at my own risk, of course).

The bulletChart has a `tooltip` accessor for customizing the tooltip's
content. If that isn't flexible enough, it also exposes the dispatch
object which is used to trigger tooltips. So bind to its `tooltipShow`
and `tooltipHide` events:

var bulletChart = nv.models.bulletChart();

bulletChart.dispatch
.on('tooltipShow', function() { ... })
.on('tooltipHide', function() { ... });

> https://github.com/novus/nvd3/blob/master/src/models/bulletChart.js#L145-L155
> Or let's say that I wanted to change the '1em' here. Again, I have to copy
> the entire file and modify it. If it was using the OO pattern and the
> highlighted code inside a renderTitle method I could subclass, override, and
> then call super.

You can override this attribute directly, no subclassing necessary.

selection
.call(bulletChart)
.selectAll('.nv-subtitle')
.attr('dy', '2em');

I don't see any official documentation that confirms that the `nv-`
prefixed class names are part of the public API, but if flexible reuse
is a design goal, I think they need to be.

Chris Viau

unread,
Dec 23, 2012, 1:36:36 PM12/23/12
to d3...@googlegroups.com
Yes, the reusable pattern is just one pattern particularly suited to fit with the d3 source code. But dc.js, xChart and Rickshaw don't use the reusable pattern and are awesome libs.

D3 (and NVD3) is very functional and tend to prefer mixins and composition over inheritance. I found it very hard but rewarding to change my mindset from OO to functional. What you want could all be done with NVD3 and the reusable API, as @john pointed out. For example, for overriding force-direct computation variables, you could indeed refactor the source code to expose these variables or to allow other algorithms to be plugged-in. But once again, you could also bypass the fd-layout and compute your own, setting the position at each ticks of your own loop and wrap it in a plugin, and apply a different set of styles to different filtered selection, etc. If you could abstract out a specific example on a tributary, jsfiddle or bl.ocks, it would be a very interesting exercise to convert it to the reusable API style and compare it to the OO solution. 




On Sun, Dec 23, 2012 at 8:26 AM, <ben.p....@gmail.com> wrote:
The pattern described in http://bost.ocks.org/mike/chart/ seems like a bit of an anti-pattern to me, at least when it comes to sharing extensible code with others (even within the same team). By design one cannot modify the internals of the closure. One can't even override the setters/getters externally because new getters/setters will be scoped to the point of their definition.

As an example of the challenges the pattern poses here's some code from NVD3, a library that uses this pattern...

(Contrived examples: I know the authors chose to make these private)

Let's say that I wanted to change the showTooltip method in the first link for just one of my charts, my only option is to copy the entire chart to a new js file and make my changes. But if this were developed with a classical OO pattern I would be able to subclass the chart and override the showTooltip method (at my own risk, of course).

Or let's say that I wanted to change the '1em' here. Again, I have to copy the entire file and modify it. If it was using the OO pattern and the highlighted code inside a renderTitle method I could subclass, override, and then call super.

With these examples one could argue that the authors have just been too aggressive about what is private (or that I am being to greedy in customization), but I would argue that private abuse (heh) is actually what is encouraged by this pattern and that when it occurs there is little recourse for the user.

Here's another example. At my company we have attempted to create a base class for our force-directed network graphs. My team and I want subclass this to suit the needs of the implementation. (User-configurable physics, differences in visual representation of the nodes and edges, etc.) The reusable chart pattern breaks down pretty hard for us in this scenario of shared functionality. 

Has anyone encountered these difficulties? I'm interested in thoughts from the D3 community on this.


PS - Thank you Mike and team for D3. It is amazing.
PPS - I'm not trying to pick on NVD3. It is also amazing.

Marc Fawzi

unread,
Dec 23, 2012, 1:45:03 PM12/23/12
to d3...@googlegroups.com, d3...@googlegroups.com
Check out this pattern which is basically the same as Mike's (in as far as I can see in terms of the resulting functionality) but offers some improvements like automatic setter/getter construction and improved level of abstraction (inside the component)


Let me know if you see any hard issues with it, or if u have any feedback at all.

Thanks

Sent from my iPhone

Simon Russell

unread,
Dec 23, 2012, 3:44:49 PM12/23/12
to d3...@googlegroups.com

As others have said, the design does have ways to achieve the outcomes you're looking for, but perhaps the particular implementations you're looking at don't.

Customisation by inheritance is itself generally an antipattern. Composition etc generally get to a design that's easier to scale and maintain.

In your examples, those overrides would need to be part of the public API, or you'd find your code breaking at a later point. They could equally be part of the public API in the reusable charts design.

That said, I haven't been using the pattern in my charts; the other day I sat down to change some of them over, but I'm not sure if it suits my application. There's nothing that says you should use the pattern, I think it was an attempt to get some sort of consistency in the d3 ecosystem.

(I've been using CoffeeScript classes with a simple interface and a compositional design.)

Some of the libs could do with being more extensible; I don't much like the "do the chart and fix up the attributes you don't like" approach. But there's always forking and pull requests; that's the advantage of open source.

Marc Fawzi

unread,
Dec 23, 2012, 6:30:29 PM12/23/12
to d3...@googlegroups.com, d3...@googlegroups.com
The reason why you'd want to work with the D3 reusable component pattern and why it's worth it is because you may want your components to work and behave the same way as the built-in D3 components (talking lower level components here, but generally any built in component) This does not mean that they couldn't be more flexible but that they simply implement the general D3 patterns for configuration and usage (your requirements would be satisfied in addition to this not in place of it) This way you're not introducing a new mix of patterns and not creating two levels of abstraction (D3 and your component architecture) but just one augmented abstraction. 

It's pretty subjective as far as all that goes. 

Sent from my iPhone

Ben

unread,
Dec 24, 2012, 12:15:06 PM12/24/12
to d3...@googlegroups.com
Wow. Thanks for all the great responses, john, chris, marc, and simon! I can see the advantages of this pattern now. It may not be right for all situations, but it is definitely not an anti-pattern.

I stupidly deleted my original post. Even though it shows up in everyone's response as "quoted text" I'll add it back right here for people who may come to this thread later. :-/

-------

The pattern described in http://bost.ocks.org/mike/chart/ seems like a bit of an anti-pattern to me, at least when it comes to sharing extensible code with others (even within the same team). By design one cannot modify the internals of the closure. One can't even override the setters/getters externally because new getters/setters will be scoped to the point of their definition.
As an example of the challenges the pattern poses here's some code from NVD3, a library that uses this pattern...
(Contrived examples: I know the authors chose to make these private)
https://github.com/novus/nvd3/blob/master/src/models/bulletChart.js#L39-L45
Let's say that I wanted to change the showTooltip method in the first link for just one of my charts, my only option is to copy the entire chart to a new js file and make my changes. But if this were developed with a classical OO pattern I would be able to subclass the chart and override the showTooltip method (at my own risk, of course).
https://github.com/novus/nvd3/blob/master/src/models/bulletChart.js#L145-L155
Or let's say that I wanted to change the '1em' here. Again, I have to copy the entire file and modify it. If it was using the OO pattern and the highlighted code inside a renderTitle method I could subclass, override, and then call super.
With these examples one could argue that the authors have just been too aggressive about what is private (or that I am being to greedy in customization), but I would argue that private abuse (heh) is actually what is encouraged by this pattern and that when it occurs there is little recourse for the user.
Here's another example. At my company we have attempted to create a base class for our force-directed network graphs. My team and I want subclass this to suit the needs of the implementation. (User-configurable physics, differences in visual representation of the nodes and edges, etc.) The reusable chart pattern breaks down pretty hard for us in this scenario of shared functionality. 
Has anyone encountered these difficulties? I'm interested in thoughts from the D3 community on this.

PS - Thank you Mike and team for D3. It is amazing.
PPS - I'm not trying to pick on NVD3. It is also amazing.

marc fawzi

unread,
Dec 24, 2012, 7:09:08 PM12/24/12
to d3...@googlegroups.com
Hey Ben,

I've updated my pattern (which is derived from Mike's resuable chart
pattern) so that both getters and setters can operate with function
references not just directly specified values. I hope this helps.
Maybe you can let me know where it fails to meet your needs?

Here is what the updated pattern looks like

http://javacrypt.wordpress.com/2012/12/15/improvements-to-d3s-reusable-component-pattern/

var myChart = function chart(args) {

// private vars defined ordinarily
var a, b, c, d, etc;

// let's refer to the instance as obj
var obj = {};

// place to hold publicly accessible properties
obj.props = {};

// all publicly accessible properties are defined here
obj.props.width=90;
obj.props.height=80;

obj.props.area = function() {
return this.props.height * this.props.width
}

// this creates automatic getter/setter on the instance per each
public property
// so that public properties can be accessed as
instance.property(value) (when setting)
// and instance.property() (when getting), and where the value can
be a function reference
// with context set to instance
// In other words, everything we need...
for (o in obj.props) {
if (obj.props.hasOwnProperty(o)) {
obj[o] = new Function("value", "if (!arguments.length) return
typeof this.props['" + o.toString() +
"'] == 'function' ? this.props['" + o.toString() + "'].call(this)
: this.props['" + o.toString() + "'];" +
"this.props['" + o.toString() + "'] = (typeof value == 'function'
? value.call(this) : value); return this")
}
}

// this is how private methods are defined
var somePrivateMethod = function() { console.log('hi, i am a private method')}

// this is how public methods are defined...
obj.render = function() {

// generate chart here, using `obj.props.width` and `obj.props.height`

console.log('rendering chart with width = ' + obj.props.width + '
and height = ' + obj.props.height)

if (args) console.log('detected component scoped argument: ' + args)

return obj;
}

return obj;
}

// testing the original settings
myChart().render()
console.log(myChart().width(), myChart().height())

// create new instances with new settings
var chart1 = myChart()
.width(500)
.height(300)
.render()

var chart2 = myChart()
.width(200)
.height(800);
// example of rendering after setting
chart2.render()

var chart3 = myChart()
.width(100)
// example of function as value generator
.height(function() {return this.width() * 4})
.render()
var chart4 = myChart(999)
.width(100)
.height(120)
.render()

// testing the new settings
console.log(chart1.width(), chart1.height())
console.log(chart2.width(), chart2.height())
console.log(chart3.width(), chart3.height())

// set a new width for chart3
chart3.width(3000)

console.log(chart3.width(), chart3.height())

console.log(chart4.width(), chart4.height())

// tests for function reference as value for setter

// test getter for property
console.log('area property with default function', chart1.area())

// change setter for property
chart1.area(function() { return Math.pow(this.width(), 2) * (Math.PI/4)})

// test getter after setter was changed
console.log('area property with updated function', chart1.area())

marc fawzi

unread,
Dec 26, 2012, 3:44:42 PM12/26/12
to d3...@googlegroups.com
There is a little gap between what Ben wanted and what I pasted. What
I had before only allowed a function to be passed which was then
executed in the context of the component instance and its value became
the new setting. What I believe Ben was asking for is the ability to
override the setter function with a new function.

Here is the updated code for that (you can still do the former)
obj.setter = function(prop, setter) {

obj.props[prop] = setter;

return obj;
chart1.setter("area", function() { return Math.pow(this.width(), 2) *
(Math.PI/4)})

// test getter after setter was changed
console.log('area property with updated function', chart1.area())

console.log('area property with updated function after diameter is
changed', chart1.width(700).area())

marc fawzi

unread,
Dec 26, 2012, 9:39:36 PM12/26/12
to d3...@googlegroups.com
So after some real world usage, I decided to pass a double function
reference inside the object.property() for overriding setter/getter
and pass a single function reference for changing the property value,
and the latter is only when a literal value is not possible or not
preferred

http://javacrypt.wordpress.com/2012/12/15/improvements-to-d3s-reusable-component-pattern/

No more .setter method. Pure and simple, IMO, but very eager to see
where it might fail.

Includes test with closure

var myChart = function chart(args) {

// private vars defined ordinarily
var a, b, c, d, etc;

// let's refer to the instance as obj
var obj = {};

// place to hold publicly accessible properties
obj.props = {};

// all publicly accessible properties are defined here
obj.props.width=undefined;
obj.props.height=undefined;

obj.props.area = function() {
return this.props.height * this.props.width
}

// this creates automatic getter/setter on the instance per each
public property
// so that public properties can be accessed as
instance.property(value) (when setting)
// and instance.property() (when getting), and where the value can
be a function reference
// with context set to instance
// set property to function reference (use only one function
reference if assigning by value)
// else use two as shown
chart1.area(function() {return function() { return
Math.pow(this.width(), 2) * (Math.PI/4)}})

// test getter after change
console.log('area property for chart1 with updated function', chart1.area())

console.log('area property for chart1 with updated function after
width is changed', chart1.width(700).area())

console.log('area property for chart2 with original function', chart2.area())

// test assigning a closure to a property
function testClosure() {
var someFunction = function() {
return ~~(Math.random() * 1000);
}
return function() {
return someFunction();
}
}

chart1.width(testClosure)

console.log(chart1.width())
console.log(chart1.width())
console.log(chart1.width())

Curran

unread,
May 28, 2015, 8:19:48 PM5/28/15
to d3...@googlegroups.com
Hello,

I'm wondering, are people still using this "towards reusable charts" pattern from 2012 today? Has this approach stood the test of time for developing real-world visualization components, or have other patterns for encapsulating reusable visualizations become preferred over this one?

I'm curious particularly about whether or not this pattern can support multiple visualizations on one page linked together via interactions, or if another framework / level of abstraction is required for this. I have developed an approach for reusable visualizations using model.js that is amenable to generalization, but I feel there should be a pure JS pattern for achieving the same results without requiring any additional library (like Model.js, or Angular, or React, or Backbone, ...).

One disadvantage I see with the "towards reusable charts" pattern is that the user of the API needs to explicitly invoke the render function after any configuration parameters change. It seems like the approach of "re-render everything whenever anything changes" is not ideal and leads to unnecessary computation (and a massive render function that has too many responsibilities), but perhaps this unnecessary computation is not really that costly in practice, and perhaps it's nice to have all the rendering logic in one place for ease of debugging. Also, I wonder how common patterns (e.g. margin convention) could be factored out of components into a common library. I have seen Marc Fawzi's "Improvements to D3’s Reusable Component Pattern", which is really great, but also is from 2012, and I'm wondering if there have been any more improvements to this.

The motivation for these questions comes from a project I am working on that assembles interactive visualizations based on a dynamic configuration called Chiasm. I am searching for a viable format for authoring visualization "plugins" for Chiasm. So far all of the plugins are using Model.js, but I feel like it should be possible to create visualization plugins for Chiasm without using Model.js.

I want to enable an ecosystem of interoperable visualization components to grow (much like the gulp ecosystem), and have Chiasm as a kind of "application state kernel" that deals with layout, state configuration, and linking visualizations together. This would ultimately enable a visualization system with features like undo-redo and real-time synchronization, where application authors could draw from a large pool of reusable visualization components, and anyone could author their own custom component.

I'm also curious if the "towards reusable charts" pattern can support optional visualization parameters that can either have defaults, or be data-driven, depending on the configuration at runtime (e.g. size and color on a scatter plot).

Thanks in advance for any input.

Best regards,
Curran
Reply all
Reply to author
Forward
0 new messages