2011-11-29 213 views
9

我有這種情況,我有兩組數據之間的父子關係。我有一個父文檔集合和一個子文檔集合。要求是父母及其相應的孩子需要出口到一個pdf文檔。一個簡單的實現上述情況可以像(Java上下的僞代碼如下所示)如下:重構嵌套for循環

for(Document parentDocument:Documents){ 
    ExportToPdf(parentDocument); 
    for(Document childDocument:parentDocument.children()){ 
     AppendToParentPdf(childDocument); 
    } 
} 

東西如上面可能會解決這個問題,但一下子要求的變化和現在的每一種父母和他們對應的孩子需要在單獨的PDF文件,所以上面給出的片段是通過改變AppendToParentPdf()ExportToPdf()如下修改:

for(Document parentDocument:Documents){ 
    ExportToPdf(parentDocument); 
    for(Document childDocument:parentDocument.children()){ 
     ExportToPdf(childDocument); 
    } 
} 

沿着這條道路前進,將需要很長時間這個看似微不足道的段之前會遭受一些嚴重的代碼異味。

我的問題,SO是:

  1. 是否存在父子關係的更好的表示,如上述的地方,而不是暴力破解我的方式,通過在O(n^2)時尚的所有文件和他們的孩子,我可以使用不同的數據結構或技術以更優化的方式遍歷整個結構。

  2. 在上面描述的場景中,業務規則對於導出pdf的方式非常流暢,是否有更智能的方法來編寫導出函數的性質?導出格式也是暫時的。 PDF可以讓位給* .docs/csvs/xmls等。

這將是很好的一些觀點。

感謝

+0

關於你提到的第一個問題,它並不像你是暴力破解,因爲你只檢索parentDocument.Children規定每位家長和這包含所有的孩子文件特定父的名單。所以你的時間複雜度不是o(n^2),而是o(n + m),其中n和m分別是父文件和子文件的計數。 – Santhosh

+0

@sc_ray你應該說明'childDocument'是否可以有多個'parentDocument'。 – Alderath

+0

@Santosh - 我不知道這是怎麼了O(N + M)的問題,因爲循環需要通過每米兒童n次的迭代,給它O(N * M)或O(n的時間複雜度^ 2)。 –

回答

3

您可以封裝你想在一個處理一個文檔做什麼。這也將允許您定義將來可以傳遞給現有代碼的新處理程序。

interface DocumentHandler { 
    void process(Document d); 
} 

class ExportToPdf implements DocumentHandler { ... } 
class AppendToParentPdf implements DocumentHandler { ... } 

// Now you're just passing the interface whose implementation does something with the document 
void handleDocument(DocumentHandler parentHandler, DocumentHandler childHandler) { 
    for(Document parent : documents) { 
     parentHandler.process(parent); 

     for(Document child : parent.children()) { 
      childHandler.process(child); 
     } 
    } 
} 

DocumentHandler appendToParent = new AppendToParentPdf(); 
DocumentHandler exportToPdf = new ExportToPdf(); 

// pass the child/parent handlers as needed 
handleDocument(exportToPdf, appendToParent); 
handleDocument(exportToPdf, exportToPdf); 

至於效率,我會說不要優化,除非遇到性能問題。在任何情況下,問題都不是嵌套循環,而是處理文檔的邏輯本身。

+0

謝謝。這是一個非常優雅的片段。 –

+0

謝謝!很高興有幫助:) – armandino

1

我試圖融入評論這一點,但有太多可說的......

我沒有看到你在談論的變化是怎樣一個代碼味道。如果這個簡單功能的需求發生變化,那麼它們就會改變。如果你只需要在一個地方做出改變,那聽起來你做得很好。如果您的客戶需要兩種方法(或更多),那麼您可能會考慮某種策略模式,因此您不必重寫周圍的代碼即可執行任何功能。

如果你每星期做出幾十次這些變化,那麼它可能會變得混亂,你可能應該制定一個計劃,以更有效地處理一個非常繁忙的變化軸。否則,紀律和重構可以幫助你保持清潔。

至於n2是否是一個問題,這取決於。 n有多大?如果你必須經常這樣做(即每小時幾十次),並且n在1000以上,那麼你可能會遇到問題。否則,只要滿足或超出需求並且CPU /磁盤利用率超出危險區域,我就不會冒汗。

+0

感謝您的回答。問題是,在任何給定的時間點,300多個用戶可能會出口100萬以上的文件(父母+孩子),儘管沒有人希望任何種類的亞秒級出口商魔法發生,但該系統有可能成爲這種預計負載徵稅。 –

+0

即使有一百萬份文檔,用戶在給定請求中要請求多少個文檔?一次將有多少用戶在系統中,其中有多少用戶同時生成文檔?除非你有一些不尋常的要求,否則我會希望這些數字能夠合理地解決。儘管如此,如果您擔心需要完成大量工作,那麼您可能需要考慮工作隊列來限制將發生的併發工作量。我不擔心,直到一些負載測試證明我需要它們。 – Bill

1

第二個問題的問題可以通過簡單地創建一個inteface Exporter與方法 export(Document doc);,然後對每種不同的格式實現。 class DocExporterImpl implements Exporter

第一個取決於您的要求,沒有任何設計模式可以解決這些問題。無法幫助你。

2

爲了您的第二個問題,你可以使用provider pattern或它的一個擴展。

提供商模式:這個模式有它的根在戰略模式,它可以讓你設計出一個抽象的數據和行爲,這樣就可以隨時換出實施

4

是否有更好地表達親子關係,比如上述,而不是勉強以O(n^2)方式通過所有文件和他們的孩子。

這不是O(N^2)。它是O(N)其中N是父文件和子文件的總數。假設沒有孩子有多個父母文檔,那麼你不能顯着提高性能。而且,與生成PDF的調用的代價相比,遍歷的代價可能微不足道。

,你可能要考慮優化的唯一情況是,如果子文檔可以是多個父母的子女。在這種情況下,您可能需要跟蹤已生成PDF的文檔,並在遍歷中重新訪問時跳過它們。可以使用HashSet執行「我之前看過這個文檔」的測試。

+0

感謝您的回答。我不明白兩個嵌套for循環如何使時間複雜度O(n + m)或O(n)代替n^2。外循環的執行將暫停,直到內循環完成執行,除非java執行了一些非常激進的循環展開,將n^2變成n。 –

1

使用了一套跟蹤哪些元素已出口可能不是最漂亮的解決方案,但它會阻止出口的兩倍的文件。

Set<Document> alreadyExported = new HashSet<Document>(); 

for(Document parentDocument:Documents){ 
    ExportToPdf(parentDocument); 
    for(Document childDocument:parentDocument.children()){ 
     if(!aldreadyExported.contains(childDocument)){ 
     ExportToPdf(childDocument); 
     alreadyExported.add(childDocument); 
     } 
    } 
}