3

假设我有大量的文件名my_dir/my_subdir,以某种方式格式化:

data11_7TeV.00179691.physics_Egamma.merge.NTUP_PHOTON.f360_m796_p541_tid319627_00
data11_7TeV.00180400.physics_Egamma.merge.NTUP_PHOTON.f369_m812_p541_tid334757_00
data11_7TeV.00178109.physics_Egamma.merge.D2AOD_DIPHO.f351_m765_p539_p540_tid312017_00

例如data11_7TeVdata_type00179691运行号、NTUP_PHOTON数据格式。

我想写一个接口来做这样的事情:

dataset = DataManager("my_dir/my_subdir").filter_type("data11_7TeV").filter_run("> 00179691").filter_tag("m = 796");                     
// don't to the filtering, be lazy
cout << dataset.count();                          // count is an action, do the filtering
vector<string> dataset_list = dataset.get_list(); // don't repeat the filtering
dataset.save_filter("file.txt", "ALIAS");         // save the filter (not the filenames), for example save the regex
dataset2 = DataManagerAlias("file.txt", "ALIAS"); // get the saved filter
cout << dataset2.filter_tag("p = 123").count();

我想要懒惰的行为,例如在像countor之类的任何操作之前不必进行真正的过滤get_list。如果已经完成,我不想重做过滤。我只是在学习一些关于设计模式的东西,我想我可以使用:

  • AbstractFilter实现filter*方法的抽象基类
  • 工厂从被调用的方法中决定使用哪个装饰
  • 每次我调用filter* 方法时,我都会返回一个装饰类,例如:

AbstractFilter::filter_run(string arg) {
    decorator = factory.get_decorator_run(arg);  // if arg is "> 00179691" returns FilterRunGreater(00179691)
    return decorator(this);
}

  • 构建正则表达式以过滤文件名的代理,但不进行过滤

我也在学习 jQuery,我正在使用类似的链接机制。

有人可以给我一些提示吗?有什么地方可以解释这样的设计吗?设计必须非常灵活,特别是要处理文件名中的新格式。

4

4 回答 4

3

我相信您将设计模式方面过度复杂化并掩盖了潜在的匹配/索引问题。从磁盘获取完整的目录列表可能比它返回的文件名的内存过滤要贵几个数量级,并且前者需要在您执行 acount()get_list()任何操作之前完成dataset(尽管您可以想出上的一些惰性迭代器操作dataset)。

如前所述,真正的功能挑战可能是索引文件名,以便您可以快速重复地找到匹配项。但是,即使这样也不太可能,因为您可能从获取文件名数据集到实际打开这些文件,这再次慢了几个数量级。因此,索引的优化可能不会对您的整个程序的性能产生任何明显的影响。

但是,假设您将所有匹配的目录条目读入数组 A。

现在,对于过滤,似乎您的要求通常可以使用std::multimap find(),lower_bound()和来满足upper_bound()。最通用的方法是为数据类型、运行编号、数据格式、p值、mtid等设置单独的多重映射,这些映射映射到 A 中的索引列表。然后,您可以使用现有的 STL 算法来查找索引对您的各个过滤器的结果是通用的。

如果您碰巧对数据和过滤需求有未说明的见解/限制(这很可能),则可以进行很多优化。例如:

  • 如果您知道将始终使用特定过滤器,并立即将潜在匹配减少到可管理的数量(例如 < ~100),那么您可以先使用它并诉诸暴力搜索以进行后续过滤。

另一种可能性是将单个文件名的属性提取到一个结构中:std::string data_type; std::vector<int> p;等等,然后编写一个表达式评估器,支持诸如“p包括924和data_type =='XYZ'”之类的谓词,尽管它本身有助于蛮力比较而不是更快的基于索引的匹配。

我知道您说过您不想使用外部库,但是如果您的需求确实处于更复杂的范围内,那么内存数据库和类似 SQL 的查询功能可能会为您省去很多麻烦。

于 2011-05-27T05:11:11.257 回答
1

我会使用策略模式。您的 DataManager 正在构造一个 DataSet 类型,并且该 DataSet 分配了一个 FilteringPolicy。默认值可以是 NullFilteringPolicy,这意味着没有过滤器。如果调用 DataSet 成员函数 filter_type(string t),它会将过滤策略类换成新的。新的可以通过 filter_type 参数在工厂构造。filter_run() 等方法可用于将过滤条件添加到 FilterPolicy。在 NullFilterPolicy 情况下,它只是无操作。这对我来说似乎很简单,我希望这会有所帮助。

编辑:要解决方法链接,您只需返回 *this; 例如,返回对 DataSet 类的引用。这意味着您可以将 DataSet 方法链接在一起。这就是实现 operator>> 或 operator<< 时 c++ iostream 库所做的事情。

于 2011-05-23T21:57:09.850 回答
1

首先,我认为您的设计非常聪明,并且非常适合您尝试建模的那种行为。

无论如何,我的理解是,您正在尝试并构建一种“领域特定语言”,您可以在其中链接表示动作的“动词”(各种过滤方法)或连接“实体”(其中可变性由不同的可能存在的命名格式,尽管您对此没有说任何话)。

在这方面,在 Martin Flowler 的《 Domain Specific Languages 》一书中发现了一个非常有趣的讨论。只是为了让您了解它的含义,在这里您可以找到关于“方法链”模式的有趣讨论,定义为:

“使修饰符方法返回宿主对象,以便可以在单个表达式中调用多个修饰符。”</p>

如您所见,此模式描述了您在设计中设置的链接机制。

在这里,您可以找到在定义此类 DSL 时发现的所有有趣模式的列表。同样,您会很容易地找到一些专门的模式,这些模式也在您的设计中暗示或描述为更通用的模式(如装饰器)。其中一些是:Regex Table Lexer、Method Chaining、Expression Builder 等。还有更多可以帮助您进一步指定设计的工具。

总而言之,我可以通过说我在您的规范中看到“命令处理器”模式的位置来添加我的盐,但我非常有信心通过部署 Fowler 提出的强大抽象,您将能够提出具有更具体和精确的设计,涵盖了目前被 GoF 模式集的“普遍性”简单隐藏的问题的各个方面。

确实,对于您所描述的问题,这可能是“矫枉过正”,但作为面向模式设计的练习,它可能非常有见地。

于 2011-05-30T21:20:49.733 回答
0

我建议从 boost 迭代器库开始 - 例如filter iterator

(当然,boost 包含一个非常好的正则表达式库。)

于 2011-05-14T20:37:00.867 回答