面向对象编程
--再谈文本查询示例
引言:
扩展第10.6节的文本查询应用程序,使我们的系统可以支持更复杂的查询。
为了说明问题,将用下面的简单小说来运行查询:
Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. "Daddy, shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy, is there "
系统应该支持:
1)查找单个单词的查询。按升序显示所有包含该单词的行:
Executed Query for: Daddy match occurs 3 times: (line 2) Her Daddy says when the wind blows (line 7) "Daddy, shush, there is no such thing," (line 10) Shyly, she asks, "I mean, Daddy, is there "
2)“非”查询,使用~操作符。显示所有不匹配的行:
Executed Query for: ~(Alice) match occurs 9 times: (line 2) Her Daddy says when the wind blows (line 3) through her hair, it looks almost alive, (line 4) like a fiery bird in flight. ...
3)“或”查询,使用|操作符。显示与两个查询条件中任意一个匹配的所有行:
Executing Query for: (hair | Alice) match occurs 2 times: (line 1) Alice Emma has long flowing red hair. (line 3) through her hair, it looks almost alive,
4)“与”查询,使用&操作符。显示与两个查询条件都匹配的所有行:
Executed query: (hair & Alice) match occurs 1 time: (line 1) Alice Emma has long flowing red hair.
而且,可以组合这些元素,如
fiery & bird | wind
我们将在 C++程序中创建这些表达式,因此,将用常规C++优先级规则对诸如此类的复合表达式求值。这个查询的求值结果将与出现的fiery和 bird的行或者出现wind的行相匹配,而不会与fiery或 bird单独出现的行相匹配:
Executing Query for: ((fiery & bird) | wind) match occurs 3 times: (line 2) Her Daddy says when the wind blows (line 4) like a fiery bird in flight. (line 5) A beautiful fiery bird, he tells her,
输出将打印查询,并使用圆括号指出解释该查询的方法。像原来的实现一样,系统必须足够聪明,不会重复显示相同行。
一、面向对象的解决方案
可以考虑使用10.6.2节的TextQuery表示单词查询,然后从TextQuery类派生其他类。
但是,这个设计可能有缺陷。概念上,“非”查询不是一种单词查询,相反,非查询“有一个”查询(单词查询或其他任意种类的查询),非查询对该查询的值求反。
注意到这一点,我们将不同种类的查询建模为独立的类,它们共享一个公共基类:
WordQuery //Shakespeare NotQuery //~Shakespeare OrQuery //Shakespeare | Marlowe AndQuery //William & Shakespeare因此,我们不继承TextQuery,而是使用TextQuery类保存文件并建立相关的word_map,使用查询类建立表达式,这些表达式对TextQuery对象中的文件运行查询。
1、抽象接口类
已经识别出四种查询类,这些类在概念上是兄弟类。它们共享相同的抽象接口,这暗示我们定义一个抽象基类以表示由查询执行的操作。将该抽象基类命名为 Query_base,以指出它的作用是作为查询继承层次的根。
直接从抽象基类派生 WordQuery 和 NotQuery 类,但是AndQuery 和 OrQuery 类具有系统中其他类所没有的一个性质:它们都有两个操作数。要为此建立模型, 将在继承层次中增加另一个名为 BinaryQuery 的抽象类,表示带两个操作数的查询。WordQuery 和 NotQuery 类将继承 BinaryQuery 类,BinaryQuery 类继承 Query_base 类。因此类的设计如图所示:
2、操作
Query_base类的存在主要是为了表示查询类型,不做实际工作。我们将重用TextQuery类以存储文件、建立查询以及查找每个单词。查询类型只需要两个操作:
1)eva l操作:返回匹配行编号的集合。该操作接受TextQuery对象,在TextQuery对象上执行查询。
2)display操作:接受ostream引用并打印给定对象在该Z http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vc3RyZWFtyc/WtNDQtcSy6dGvoaM8YnIgLz7O0sPHvavV4tCpstnX97ao0uXOqlF1ZXJ5X2Jhc2XW0LXE0Om6r8r9o6zDv7j2xcnJ+sDgtryx2NDrttTV4tCpuq/K/bao0uXX1Ly6tcSw5rG+W7K7yLsuLi6+zcO7t6jTw08ooclfockpT35doaM8YnIgLz48YnIgLz48YnIgLz62/qGi1rXQzb7ksfo8YnIgLz4Js8zQ8r2rtKbA7bzGy+Oy6dGvo6y2+LK7vajBorLp0a+jrLWrysejrNDo0qrE3Lm7tLS9qLLp0a/S1LHj1MvQ0LPM0PKho9fuvPK1pbXEsOy3qMrHseDQtEMrK7HttO/KvdaxvdO0tL2osunRr6OswP3I56O6PGJyIC8+PHByZSBjbGFzcz0="brush:java;"> Query q = Query("fiery") & Query("bird") | Query("wind");
以产生前面描述的复合查询。
这个问题描述暗示我们:用于级代码将不能直接使用我们的继承层次,相反,我们将定义一个名为Query的句柄类,用它隐藏继承层次。用户代码将根据句柄执行,用户代码只能间接操纵Query_base对象[以及Query_base的派生对象]。
像S