我遇到这种情况,我在两组数据之间有一个父子关系。我有一个父文档集合和一个子文档集合。要求是父母及其相应的孩子需要出口到'a'pdf文件中。上述情况的简单实现可以如下(以下java-ish伪代码):
for(Document parentDocument:Documents){
ExportToPdf(parentDocument);
for(Document childDocument:parentDocument.children()){
AppendToParentPdf(childDocument);
}
}
上面的内容可能会解决问题,但突然之后需求会发生变化,现在每个父母及其相应的孩子都需要在单独的pdf中,所以通过更改{{1}来修改上面给出的片段} AppendToParentPdf()
跟随:
ExportToPdf()
顺便说一句,不久之后,这个看似微不足道的代码片段就会遭遇一些严重的代码味道。
我的问题是:
是否有更好的父子关系表示,例如上面的内容,而不是以for(Document parentDocument:Documents){
ExportToPdf(parentDocument);
for(Document childDocument:parentDocument.children()){
ExportToPdf(childDocument);
}
}
方式强行通过所有文件及其子女,我可以使用不同的数据 - 结构或技术以更优化的方式遍历整个结构。
在上面描述的场景中,业务规则对于导出pdf的方式相当流畅,是否有更智能的方法来编写导出函数的性质?导出格式也是暂时的。 PDF可以让位给* .docs / csvs / xmls等。
对此有所了解。
由于
答案 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);
}
}
}