重构嵌套for循环

时间:2011-11-29 04:17:47

标签: java algorithm design-patterns export relationship

我遇到这种情况,我在两组数据之间有一个父子关系。我有一个父文档集合和一个子文档集合。要求是父母及其相应的孩子需要出口到'a'pdf文件中。上述情况的简单实现可以如下(以下java-ish伪代码):

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

上面的内容可能会解决问题,但突然之后需求会发生变化,现在每个父母及其相应的孩子都需要在单独的pdf中,所以通过更改{{1}来修改上面给出的片段} AppendToParentPdf()跟随:

ExportToPdf()

顺便说一句,不久之后,这个看似微不足道的代码片段就会遭遇一些严重的代码味道。

我的问题是:

  1. 是否有更好的父子关系表示,例如上面的内容,而不是以for(Document parentDocument:Documents){ ExportToPdf(parentDocument); for(Document childDocument:parentDocument.children()){ ExportToPdf(childDocument); } } 方式强行通过所有文件及其子女,我可以使用不同的数据 - 结构或技术以更优化的方式遍历整个结构。

  2. 在上面描述的场景中,业务规则对于导出pdf的方式相当流畅,是否有更智能的方法来编写导出函数的性质?导出格式也是暂时的。 PDF可以让位给* .docs / csvs / xmls等。

  3. 对此有所了解。

    由于

6 个答案:

答案 0 :(得分:4)

  

是否有更好的父子关系表示,例如上面的内容,而不是以O(n ^ 2)方式强行通过所有文档及其子项。

这不是O(N^2)。它是O(N),其中N是父文档和子文档的总数。假设没有孩子有多个父文档,那么就无法显着提高性能。此外,与生成PDF的调用的成本相比,遍历的成本可能微不足道。

您可能想要考虑优化的唯一情况是,子文档可以是多个父项的子项。在这种情况下,您可能希望跟踪已经为...生成PDF的文档...如果您在遍历中重新访问它们,则跳过它们。 “我之前看过这个文档”的测试可以使用HashSet实现。

答案 1 :(得分: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);

至于效率,我会说除非遇到性能问题,否则不要尝试优化。在任何情况下,问题都不在于嵌套循环,而在于处理文档的逻辑本身。

答案 2 :(得分:2)

对于第二个问题,您可以使用provider pattern或其扩展名。

  

提供者模式:此模式源于策略模式,它允许您在抽象中设计数据和行为,以便您可以随时交换实现

答案 3 :(得分:1)

我试图将其纳入评论中,但有太多话要说......

我不知道你所说的改变是如何代码味道的。如果这个简单函数的需求发生变化,那么它们就会改变。如果您只需要在一个地方进行更改,那么听起来您已经做得很好。如果您的客户端需要两种方式(或更多),那么您可能会考虑某种策略模式,因此您不必重写周围的代码来执行任一功能。

如果您每周进行数十次这些更改,那么它可能会变得混乱,您可能应该制定一个计划,以便更有效地处理非常繁忙的变革轴。否则,纪律和重构可以帮助您保持清洁。

关于n²是否是一个问题,取决于。 n有多大?如果你必须经常这样做(即每小时几十次)并且n是1000的,那么你可能会遇到问题。否则,只要您满足或超出需求并且您的CPU /磁盘利用率超出危险区域,我就不会出汗。

答案 4 :(得分:1)

只需使用该方法创建inteface Exporter即可解决第二个问题 export(Document doc);,然后针对各种格式实施,例如class DocExporterImpl implements Exporter

第一个取决于您的要求,没有设计模式可以解决这些问题。无法帮助你。

答案 5 :(得分:1)

使用Set来跟踪哪些元素已经被导出可能不是最漂亮的解决方案,但它会阻止文档被导出两次。

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);
      }  
   }
}
相关问题