需要一些访客般的设计模式

时间:2014-01-07 19:59:39

标签: javascript unit-testing design-patterns mocha

我将给你一个示例我的问题,以消除逻辑复杂性,让你专注于重要的部分。当然,这个例子有点无用......

我有一个树结构,其中节点就是那个

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

现在,我必须编写从root到数组中每个叶子的所有完整路径。 我的设计很差:

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行代码测试文件的情况下使用Mocha编写测试。此刻,你应该问为什么。原因是真实目的的复杂性,这与我的问题无关。我的目标是拥有一些我可以习惯的“可模仿的”原因。 (Java dev)。但我失败了。

你有什么模式我可以用来解决这个问题吗?我不是很擅长JS模式。 :/ 游客?制作Y组合器?这么多的可能性......

感谢您阅读我

1 个答案:

答案 0 :(得分: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.
   ...
}