这种设计模式何时会破裂?

时间:2012-07-19 20:25:23

标签: javascript design-patterns d3.js

我最近试图为d3.js编写一个插件,并且与一些可能微不足道的东西混淆了。关于如何创建可重用图表的d3's website有一个解释。模式看起来像这样(只有最重要的细节,完整代码是here):

设计模式1:来自d3网站

function timeSeriesChart() {
  var margin = {top: 20, right: 20, bottom: 20, left: 20},
      ...
      area = d3.svg.area().x(X).y1(Y),
      line = d3.svg.line().x(X).y(Y);

  function chart(selection) {
    selection.each(function(data) {

      // Convert data to standard representation greedily;
      // this is needed for nondeterministic accessors.
      data = data.map(function(d, i) {
        return [xValue.call(data, d, i), yValue.call(data, d, i)];
      });

      // Update the x-scale.
      ...

      // Update the y-scale.
      ...

      // Select the svg element, if it exists.
      var svg = d3.select(this).selectAll("svg").data([data]);
      ...

      // Otherwise, create the skeletal chart.
      var gEnter = svg.enter().append("svg").append("g");
      ...    
  }

  // The x-accessor for the path generator; xScale ∘ xValue.
  function X(d) {      }

  // The x-accessor for the path generator; yScale ∘ yValue.
  function Y(d) {      }

  chart.margin = function(_) {
    if (!arguments.length) return margin;
    margin = _;
    return chart;
  };

  chart.width = function(_) {
    if (!arguments.length) return width;
    width = _;
    return chart;
  };

  chart.height = function(_) {
    if (!arguments.length) return height;
    height = _;
    return chart;
  };

  chart.x = function(_) {
    if (!arguments.length) return xValue;
    xValue = _;
    return chart;
  };

  chart.y = function(_) {
    if (!arguments.length) return yValue;
    yValue = _;
    return chart;
  };

  return chart;
}

我毫不怀疑这种模式很强大,特别是因为它是由d3自己的创造者提出的。但是,在我遇到该帖子之前,我已经使用了以下模式一段时间没有问题(类似于一般创建插件的方式):

设计模式2:创建插件的一般方法

(function() {
    var kg = {
        version: '0.1a'
    };

    window.kg = kg;

    kg.chart = {};

    // ==========================================================
    // CHART::SAMPLE CHART TYPE
    // ==========================================================
    kg.chart.samplechart = {

        // ----------------------------------------------------------
        // CONFIGURATION PARAMETERS
        // ----------------------------------------------------------
        WIDTH: 900,
        HEIGHT: 500,
        MARGINS: {
            top: 20,
            right: 20,
            bottom: 20,
            left: 60,
            padding: 40
        },
        xRange: d3.time.scale(),
        yRange: d3.scale.linear(),
        xAxis: d3.svg.axis(),
        yAxis: d3.svg.axis(),
        data: {},

        // ----------------------------------------------------------
        // INIT FUNCTION
        // ----------------------------------------------------------
        init: function() {
            // Initialize and add graph to the given div
            this.update();
        },

        // ----------------------------------------------------------
        // Redraws the graph
        // ----------------------------------------------------------
        update: function() {
            var parentThis = this;
            var newData = parentThis.data;

            // Continue with adding points/lines to the chart


        },
        // ----------------------------------------------------------
        // Gets random data for graph demonstration
        // ----------------------------------------------------------
        getRandomData: function() {
            // Useful for demo purposes  

        }
    };

    // ==========================================================
    // HELPER FUNCTIONS
    // ==========================================================

}());


// EXAMPLE: This renders the chart. 
kg.chart.samplechart.vis = d3.select("#visualization");
kg.chart.samplechart.data = kg.chart.samplechart.getRandomData();
kg.chart.samplechart.init();​    

我一直在使用设计模式2 一段时间没有任何问题(我同意它不是超级干净但我正在努力)。在看了设计模式1 之后,我觉得它有太多的冗余。例如,查看使内部变量可访问的最后一段代码(chart.margin = function(_) {}等)。

也许这是一种很好的做法但它会使维护变得繁琐,因为必须针对每种不同的图表类型重复这一过程(如此处在名为NVD3的库中所见,目前正在开发中),并增加了开发工作量和风险。错误。

我想知道如果我继续使用我的模式或者我的模式如何改进或接近设计模式1 的精神,我会面临什么样的严重问题。我试图避免在这一点上改变模式,因为这需要完全重写并将新的错误引入到一个稍微稳定的迷你库中。有什么建议?

1 个答案:

答案 0 :(得分:3)

实际上,您可以在d3源代码中找到第二个模式。

(function(){
...
d3 = {version: "2.9.6"}; // semver
...

d3.svg = {};
d3.svg.arc = function() {
  var innerRadius = d3_svg_arcInnerRadius,
      outerRadius = d3_svg_arcOuterRadius,
...

但是组件和生成器,如比例,轴,区域和布局,倾向于使用我们可以称为“图表作为具有getter-setter方法的闭包”或“高阶编程”的模式 通过可配置的功能“。您可以在Google Group thread上按照理论基础进行讨论。

就个人而言,我也不喜欢这种冗余,即使它有用且相当可读。所以我使用自定义函数自动生成这些getter和setter:

d3.helper.createAccessors = function(visExport) {
    for (var n in visExport.opts) {
        if (!visExport.opts.hasOwnProperty(n)) continue;
        visExport[n] = (function(n) {
            return function(v) {
                return arguments.length ? (visExport.opts[n] = v, this) : visExport.opts[n];
            }
        })(n);
    }
};

我在图表模块的末尾使用了这个:

d3.helper.createAccessors(chart, opts);

其中opts是所有公共函数的名称:

var opts = {
            width: 200,
            margin: [5, 0, 20, 0],
            height: 200,
            showPoints: true,
            showAreas: false,
            enableTooltips: true,
            dotSize: 4
        };

以下是一个完整的示例:http://jsfiddle.net/christopheviau/YPAYz/