继承体系设计

本文介绍了如何设计和实现一个面向文本查询的抽象基类Query_base,以及其派生类如WordQuery、NotQuery和二元查询类AndQuery、OrQuery。核心是eval函数,通过虚函数处理不同类型的查询逻辑。
//是一个抽象基类,具体的查询类型从中派生
//不希望用户或者派生类直接使用Query_base,而是通过Query,所有成员都是private的
class Query_base {
	friend class Query;	//Query需要调用Query_base的虚函数,声明为友元
protected:
	using line_no = TextQuery::line_no;	//用于eval函数
	virtual ~Query_base() = default;	//隐式地在派生类析构函数中使用
private:
	//eval返回与当前Query匹配的QueryResult
	virtual QueryResult eval(const TextQuery&) const = 0;
	//rep是表示查询的一个string
	virtual std::string rep() const = 0;
};
//这是一个管理Query_base继承体系的接口类
//Query类对外提供接口,同时隐藏了Query_base的继承体系
class Query {
	//这些运算符需要访问接受shared_ptr的构造函数,而该函数是私有的
	friend Query operator~(const Query&);
	friend Query operator|(const Query&, const Query&);
	friend Query operator&(const Query&, const Query&);
public:
	//构建一个新的WordQuery
	Query(const std::string& s) : q(new WordQuery(s)) {}	
	//接口函数:调用对应的Query_base操作
	Query_base eval(const TextQuery& t) const {
		return q->eval(t);
	}
	std::string rep() const {
		return q->rep();
	}
	std::ostream& operator<<(std::ostream& os, const Query& query) {
		//Query::rep通过它的Query_base指针对rep()进行了虚调用
		return os << query.rep();
	}
private:
	//私有的,不希望一般的用户代码能随便定义Query_base对象
	Query(std::shared_ptr<Query_base> query) : q(query) {}
	std::shared_ptr<Query_base> q;
};
class WordQuery : public Query_base {
	friend class Query;	//Query使用WordQuery构造函数
	WordQuery(const std::string& s) : query_word(s) {}
	//具体的类:WordQuery将定义所有继承而来的纯虚函数
	QueryResult eval(const TextQuery& t) const {
		return  t.query(query_word);
	}
	std::string rep() const {
		return query_word;
	}
	std::string query_word;		//要查找的单词
};
//直接使用一个Query对象,就像用户代码可以通过接口类得到简化一样,直接用接口类简化自己的类
class NotQuery : public Query_base {
	//成员私有,设置成友元
	friend Query operator~(const Query& operand) {
		//隐式地使用接受一个shared_ptr<Query_base>的Query构造函数
		return std::shared_ptr<Query_base>(new NotQuery(operand));
	}
	NotQuery(const Query& q) : query(q) {}
	//具体的类:NotQuery将定义所有继承而来的纯虚函数
	std::string rep() const {
		return "~(" + query.rep() + ")";	//最终执行的为虚调用
	}
	QueryResult eval(const TextQuery&) const;
	Query query;
};
//没有定义eval,而是继承该虚函数,因此也是一个抽象基类
class BinaryQuery : public Query_base {
protected:
	BinaryQuery(const Query& l, const Query& r, std::string s) : 
		lhs(l), rhs(r), opSym(s) {}
	std::string rep() const {
		return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";
	}
	Query lhs, rhs;	//左侧和右侧运算对象
	std::string opSym;	//运算符名字
};

//将运算符定义为友元
//构造函数通过运算符创建BinaryQuery基类部分
class AndQuery : public BinaryQuery {
	friend Query operator&(const Query& lhs, const Query& rhs) {
		return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs));
	}
	AndQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "&") {}
	QueryResult eval(const TextQuery&) const;
};
class OrQuery : public BinaryQuery {
	friend Query operator|(const Query& lhs, const Query& rhs) {
		return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs));
	}
	OrQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "|") {}
	QueryResult eval(const TextQuery&) const;
};
//eval函数是我们这个查询系统的核心。每个eval 函数作用于各自的运算对象,同时遵循的内在逻辑也有所区别
QueryResult OrQuery::eval(const TextQuery& text) const {
	//通过Query成员lhs和rhs进行的虚调用
	//调用veal返回每个运算对象的QueryResult
	auto right = rhs.eval(test), left = lhs.eval(text);
	//将左侧运算对象的行号拷贝到结果set中
	auto ret_lines = std::make_shared<set<line_no>>(left.begin(), left.end());
	//插入右侧运算对象所得的行号
	ret_lines->insert(right.begin(), right.end());
	//返回一个新的QueryResult,它表示lhs和rhs的并集
	return QueryResult(rep(), ret_lines, left.get_file());//right和left指向的是同一文件
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值