2014-01-07 53 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