javascript - 这种设计模式什么时候会被打破?

标签 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一段时间了,没有任何问题(我同意它不是 super 干净,但我正在努力)。看完设计模式1后,我只是觉得它冗余太多了。例如,查看使内部变量可访问的最后代码块(chart.margin = function(_) {} 等)。

也许这是一个很好的做法,但它使维护变得麻烦,因为必须对每种不同的图表类型重复此操作(如在当前正在开发的名为 NVD3 的库中看到的),并且增加了开发工作量和错误风险。

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

最佳答案

事实上,您可以在 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 是所有公共(public)函数的名称:

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

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

关于javascript - 这种设计模式什么时候会被打破?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11568751/

相关文章:

javascript - 如何获取选择元素的选项值并将其用于计算?

javascript - 如何为 jquery i18n 设置字符集 utf-8?

oop - 差异聚合、相识和组合(如四人帮所用)

c# - C#中的策略模式

javascript - 个性化 d3 x 轴标签

javascript - 为什么我的脚本没有在 div 中动态显示输入字符

Javascript Concat 多维数组

algorithm - 哪种算法可用于检测时间模式?

javascript - 在 dc.js 中为饼图标签添加百分比

html - Bootstrap 不允许我将数据与其他库一起使用