设计可自定义的字符串过滤器

时间:2011-05-14 09:45:17

标签: c++ design-patterns filter decorator proxy-pattern

假设我在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();

我想要懒惰的行为,例如在countget_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 个答案:

答案 0 :(得分:3)

我相信你过度复杂的设计模式方面和对基础匹配/索引问题的掩饰。从磁盘获取完整目录列表可能比它返回的文件名的RAM内过滤要贵几个数量级,前者需要先完成才能执行count()或{{1}在任何get_list()上(尽管你可以在dataset上提出一些更懒惰的迭代器操作)。

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

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

现在,对于过滤,通常可以使用dataset std::multimapfind()lower_bound()来满足您的要求。处理它的最常用方法是为数据类型,运行编号,数据格式,upper_bound()值,p值,m等分别设置多个映射到索引列表在A.您可以使用现有的STL算法来查找各个过滤器结果所共有的索引。

如果您碰巧根据您的数据和过滤需求(非常有可能)提供未说明的见解/限制,那么可以进行大量优化。例如:

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

另一种可能性是将单个文件名的属性提取到结构中: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包含一个非常好的正则表达式库。)