2014-01-07 40 views
1

我会给你一个我的问题的示例,以消除逻辑复杂性并让你专注于重要部分。当然,这个例子将是一个有点用处...需要一些类似访问者的设计模式

我有一个树形结构,其中节点都是这样

{ 
    path: "...", 
    childs : [] 
} 

现在,我必须从根所有的完整路径写入每个叶中数组。 我的设计是非常差:

function listPaths(node) { 
    var result = []; 

    function listForNode(n, parentFullPath) { 
     var thisPath = parentFullPath + "/" + n.path; 
     result.push(thisPath); 
     n.childs.forEach(function (child) { 
      listForNode(child, thisPath); 
     }); 
    } 

    listForNode(node, ""); 
    return result; 
} 

这可能是不错,但我不能写与摩卡的测试,而无需一个疯狂的600行代码的测试文件。在这一刻,你应该问为什么。原因是真实目的的复杂性,这与我的问题无关。我的目标是有一些'嘲笑'因为我习惯了。 (Java dev)。但我失败了。

您是否有任何可用于解决此问题的模式?我不擅长JS模式。 :/ 访客?制作Y组合器?这么多的可能性...

感谢您阅读我

+0

您能否更详细地解释一下您试图实现的“可嘲笑”方面?我想我正在试图理解600和600行代码测试文件是如何相关的。是否有关于JS对象上没有方法被调用的事实,所以很难嘲笑递归树访问函数? –

回答

1

你需要记住,函数是在JavaScript中的一等公民。

我看,基本上你有什么是一样的东西

function createVisitor(parentsAccumulatorInitialValue, parentsAccumulator){ 

    var visitor = function myVisitor (node) { 
     var result; 
     function listForNode(n, parentsAcc) { 
      var thisPath = parentsAccumulator(parentsAcc, n); 
      result.push(thisPath); 
      n.childs && n.childs.forEach(function (child) { 
       listForNode(child, thisPath); 
      }); 
     } 

     result = []; 
     listForNode(node, parentsAccumulatorInitialValue()); 
     return result; 
    } 
    return visitor; 

} 

var listPaths = createVisitor(
    function parentInit() { 
     return ""; 
    }, 
    function parentAcc (parentFullPath, n) { 
     return parentFullPath + "/" + n.path;   
    }); 

但是,这不是你能照顾唯一的抽象:

function createVisitor2(
    totalAccumulatorInitialValue, 
    totalAccumulator, 
    parentsAccumulatorInitialValue, 
    parentsAccumulator){ 

    var visitor = function myVisitor (node) { 
     var total; 
     function listForNode(n, parentsAcc) { 
      var thisPath = parentsAccumulator(parentsAcc, n); 
      total = totalAccumulator(total, thisPath, n); 
      n.childs && n.childs.forEach(function (child) { 
       listForNode(child, thisPath); 
      }); 
     } 

     total = totalAccumulatorInitialValue(); 
     listForNode(node, parentsAccumulatorInitialValue()); 
     return total; 
    } 
    return visitor; 

} 

var listPaths2 = createVisitor2(
    function totalInit() { 
     return []; 
    }, 
    function totalAcc(total, thisPath, n){ 
     total.push(thisPath); 
     return total; 
    }, 
    function parentInit() { 
     return ""; 
    }, 
    function parentAcc (parentFullPath, n) { 
     return parentFullPath + "/" + n.path;   
    }); 

这可能是相当合理的,但你可以看,我已经开始难以找到这些变量的适当名称。事实上,我会说我们的函数的名称是不好的,因为它不会像我所了解的访客对象那样严格地创建任何东西。然而,它的工作(顺便说一句,我稍微修改它来处理空值以及空数组):

> listPaths({ path:"foo", 
      childs: [{path:"bar", childs: null}, {path:"bob", childs: null}]}) 

["/foo", "/foo/bar", "/foo/bob"] 

它甚至可以进一步进行修改,这样你的树木不严格,甚至具有相同的结构...但我们已经在4个参数,这不是很好。如果您的访问者创建者传递了具有所有必要方法或值的单个可扩展对象,那将会更好。例如,也许(伪代码):

function createVisitor3(opts) { 
    //assume we've defined GetDefaults() somewhere local to createVisitor3 
    // as well as assume that extend is defined somewhere that copies properties 
    // into a new object like various previously existing libraries do. 
    opts = extend({}, GetDefaults(), opts); 
    var totalAccumulatorInitialValue = opts.totalAccumulatorInitialValue; 
    var totalAccumulator = opts.totalAccumulator; 
    var parentsAccumulatorInitialValue = opts.parentsAccumulatorInitialValue; 
    var parentsAccumulator = opts.parentsAccumulator; 
    var childrenGetter = opts.childrenGetter; 
    /// etc. 
    ... 
} 
+0

它看起来不是那么简单,不像我喜欢写我的代码那么简单。但我认为这是因为我刚刚开始JS。对我来说没关系,非常感谢,我会继续努力(1-2天,我认为^^) – farvilain

+0

我承认,可能有一个更简单的使用,基于二叉树的更加面向对象的模式,存在并且可以适应或可以创建,但是需要我进行一些研究才能找到它(或创建它)。这至少给你一个开始。我更多地给了你上面的代码作为一个例子,说明如何简单地获取现有代码并使其可插入。但是,这也可能会导致一些难以管理的代码,而没有经过深思熟虑。我向你展示的方法的基础依赖于函数式编程中的累加器,这为开发人员提供了很大的权力。 – JayC