假设我在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_7TeV
是data_type
,00179691
运行编号,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();
我想要懒惰的行为,例如在count
或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,而且我正在使用类似的链接机制。
有人可以给我一些提示吗?是否有一些地方可以解释这样的设计?设计必须非常灵活,特别是要处理文件名中的新格式。
答案 0 :(得分:3)
我相信你过度复杂的设计模式方面和对基础匹配/索引问题的掩饰。从磁盘获取完整目录列表可能比它返回的文件名的RAM内过滤要贵几个数量级,前者需要先完成才能执行count()
或{{1}在任何get_list()
上(尽管你可以在dataset
上提出一些更懒惰的迭代器操作)。
如上所述,真正的功能挑战可能是索引文件名,以便您可以快速重复查找匹配项。但是,即使这样也不太可能,因为你可能会从获取文件名的数据集到实际打开这些文件,这再次减慢数量级。因此,索引的优化可能不会对整体计划的性能产生任何明显的影响。
但是,假设您将所有匹配的目录条目读入数组A。
现在,对于过滤,通常可以使用dataset
std::multimap
,find()
和lower_bound()
来满足您的要求。处理它的最常用方法是为数据类型,运行编号,数据格式,upper_bound()
值,p
值,m
等分别设置多个映射到索引列表在A.您可以使用现有的STL算法来查找各个过滤器结果所共有的索引。
如果您碰巧根据您的数据和过滤需求(非常有可能)提供未说明的见解/限制,那么可以进行大量优化。例如:
另一种可能性是将单个文件名的属性提取到结构中:tid
std::string data_type;
等,然后编写支持谓词的表达式求值程序,如“p includes 924和data_type =='XYZ'”,虽然它本身有助于蛮力比较,而不是更快的基于索引的匹配。
我知道你说你不想使用外部库,但是如果你的需求确实处于更复杂的频谱末端,那么内存数据库和类似SQL的查询功能可以为你节省很多麻烦。
答案 1 :(得分:1)
我会使用策略模式。您的DataManager正在构建DataSet类型,并且DataSet已分配FilteringPolicy。默认值可以是NullFilteringPolicy,这意味着没有过滤器。如果调用DataSet成员函数filter_type(string t),它将使用新的策略类交换过滤器策略类。新的可以通过filter_type参数进行工厂构建。像filter_run()这样的方法可用于将过滤条件添加到FilterPolicy。在NullFilterPolicy案例中,它只是无操作。这对我来说似乎过于紧张,我希望这会有所帮助。
编辑: 要解决链接方法,您只需要返回* this;例如返回对DataSet类的引用。这意味着您可以将DataSet方法链接在一起。这是实现运算符&gt;&gt;时c ++ iostream库的作用。或运算符&lt;&lt;。
答案 2 :(得分:1)
首先,我认为您的设计非常智能,并且非常适合您尝试建模的行为。
无论如何,我的理解是你正在尝试并构建一种“领域特定语言”,你可以将“动词”(各种过滤方法)链接起来代表“实体”上的动作或连接“实体”(变量是由可能存在的不同命名格式表示,尽管你没有说明这一点。)
在这方面,Martin Flowler的书“Domain Specific Languages”中有一个非常有趣的讨论。只是为了让您了解它的内容,here您可以找到关于“方法链”模式的有趣讨论,定义为:
“make modifier方法返回主机对象,以便可以在单个表达式中调用多个修饰符。”
如您所见,此模式描述了您在设计中所处理的链接机制。
Here您列出了在定义此类DSL时感兴趣的所有模式。同样,您将很容易找到几个专门的模式,您也在设计中暗示或描述为更通用的模式(如装饰器)。其中一些是:Regex Table Lexer,Method Chaining,Expression Builder等。还有更多可以帮助您进一步指定设计。
总而言之,我可以通过说我在你的指定中看到一个“命令处理器”模式的位置来添加我的盐,但我相信通过部署Fowler建议的强大抽象你将能够提出一个更加具体和精确的设计,涵盖了现在被GoF模式集的“普遍性”隐藏的问题。
对于像你所描述的那样的问题,这可能是“过度杀戮”,但作为一种模式导向设计的练习,它可以非常有洞察力。
答案 3 :(得分:0)
我建议从boost迭代器库开始 - 例如filter iterator。
(当然,boost包含一个非常好的正则表达式库。)