最简单的方法是按照@billz 的建议进行 Room 类型枚举。这种方式的问题在于,每次向系统中添加新类型的 Room 时,一定不要忘记给枚举添加一个值并使用一次。您必须确保只使用一次枚举值,每堂课一次。
但是,另一方面,基于继承的设计只有在层次结构的类型具有共同行为时才有意义。换句话说,您希望以相同的方式使用它们,而不管其类型如何。IMPO,OO/继承设计并不是更好的方法。
我做这类事情的怪异且可扩展的方式是通过typelists。
通常,您对系统中的每种类型都有不同的搜索条件。而且,在许多情况下,对于不同类型的系统,此搜索的结果是不同的(搜索豪华房间和搜索普通房间不同,您可能有不同的搜索条件和/或想要不同的搜索结果数据)。
为此,系统具有三个类型列表:一个包含数据类型,一个包含搜索条件类型,一个包含搜索结果类型:
using system_data_types = type_list<NormalRoom,LuxuryRoom>;
using search_criteria_types = type_list<NormalRoomsCriteria,LuxuryRoommsCriteria>;
using search_results_types = type_list<NormalRoomSearchResults,LuxuryRoomSearchResults>;
请注意,type_lists 以相同的方式排序。这很重要,如下所示。
所以搜索引擎的实现是:
class SearchEngine
{
private:
std::vector<VectorWrapper*> _data_lists; //A vector containing each system data type in its own vector. (One vector for NormalRoom, one for LuxuryRoom, etc)
//This function returns the vector that stores the system data type passed.
template<typename T>
std::vector<T>& _get_vector() {...} //Implementation explained below.
public:
SearchEngine() {...}//Explanation below.
~SearchEngine() {...}//Explanation below.
//This function adds an instance of a system data type to the "database".
template<typename T>
void addData(const T& data) { _get_vector<T>().push_back( data ); }
//The magic starts here:
template<typename SEARCH_CRITERIA_TYPE>//This template parameter is deduced by the compiler through the function parameter, so you can ommit it.
typename search_results_types::type_at<search_criteria_types::index_of<SEARCH_CRITERIA_TYPE>> //Return value (The search result that corresponds to the passed criteria. THIS IS THE REASON BECAUSE THE TYPELISTS MUST BE SORTED IN THE SAME ORDER.
search( const SEARCH_CRITERIA_TYPE& criteria)
{
using system_data_type = system_data_types::type_at<search_criteria_types::index_of<SEARCH_CRITERIA_TYPE>>; //The type of the data to be searched.
std::vector<system_data_type>& data = _get_vector<system_data_type>(); //A reference to the vector where that type of data is stored.
//blah, blah, blah (Search along the vector using the criteria parameter....)
}
};
搜索引擎可以如下使用:
int main()
{
SearchEngine engine;
engine.addData(LuxuryRoom());
engine.addData(NormalRoom());
auto luxury_search_results = engine.search(LuxuryRoomCriteria()); //Search LuxuryRooms with the specific criteria and returns a LuxuryRoomSearchResults instance with the results of the search.
auto normal_search_results = engine.search(NormalRoomCriteria()); //Search NormalRooms with the specific criteria and returns a NormalRoomSearchResults instance with the results of the search.
}
该引擎基于为每种系统数据类型存储一个向量。并且引擎使用存储该向量的向量。我们不能拥有指向不同类型向量的多态引用/指针,因此我们使用 a 的包装器std::vector
:
struct VectorWrapper
{
virtual ~VectorWrapper() = 0;
};
template<typename T>
struct GenericVectorWrapper : public VectorWrapper
{
std::vector<T> vector;
~GenericVectorWrapper() {};
};
//This template class "builds" the search engine set (vector) of system data types vectors:
template<int type_index>
struct VectorBuilder
{
static void append_data_type_vector(std::vector<VectorWrapper*>& data)
{
data.push_back( new GenericVectorWrapper< system_data_types::type_at<type_index> >() ); //Pushes back a vector that stores the indexth type of system data.
VectorBuilder<type_index+1>::append_data_type_vector(data); //Recursive call
}
};
//Base case (End of the list of system data types)
template<>
struct VectorBuilder<system_data_types::size>
{
static void append_data_type_vector(std::vector<VectorWrapper*>& data) {}
};
所以实现SearchEngine::_get_vector<T>
如下:
template<typename T>
std::vector<T>& get_vector()
{
GenericVectorWrapper<T>* data; //Pointer to the corresponing vector
data = dynamic_cast<GenericVectorWrapper<T>*>(_data_lists[system_data_types::index_of<T>]); //We try a cast from pointer of wrapper-base-class to the expected type of vector wrapper
if( data )//If cast success, return a reference to the std::vector<T>
return data->vector;
else
throw; //Cast only fails if T is not a system data type. Note that if T is not a system data type, the cast result in a buffer overflow (index_of<T> returns -1)
}
的构造函数SearchEngine
只使用 VectorBuilder 来构建向量列表:
SearchEngine()
{
VectorBuilder<0>::append_data_type_vector(_data_list);
}
并且析构函数只迭代删除向量的列表:
~SearchEngine()
{
for(unsigned int i = 0 ; i < system_data_types::size ; ++i)
delete _data_list[i];
}
这种设计的优点是:
搜索引擎对不同的搜索使用完全相同的界面(以不同系统数据类型为目标的搜索)。并且将数据类型“链接”到其相应的搜索条件和结果的过程是在编译时完成的。
该接口是类型安全的:调用SearchEngine::search()
返回仅基于传递的搜索条件的结果类型。在编译时检测到分配结果错误。例如:NormalRoomResults = engine.search(LuxuryRoomCriteria())
生成编译错误(engine.search<LuxuryRoomCriteria>
返回LuxuryRoomResults
)。
搜索引擎是完全可扩展的:要向系统添加新的数据类型,您只需将类型添加到类型列表中。搜索引擎的执行没有变化。