我正在为即将到来的面试练习面向对象的设计。我的问题是关于酒店预订系统的设计: - 系统应该能够返回特定类型的开放房间或返回酒店的所有开放房间。 - 酒店有许多类型的客房,如普通客房,豪华客房,名人客房等。
到目前为止,我已经提出了以下课程:
Class Room{
//Information about room
virtual string getSpecifications(Room *room){};
}
Class regularRoom: public Room{
//get specifications for regular room
}
Class luxuryRoom: public Room{
//get specifications for regular room
}
//Similarly create as many specialized rooms as you want
Class hotel{
vector<Room *>openRooms; //These are all the open rooms (type casted to Room type pointer)
Public:
Room search(Room *aRoom){ //Search room of a specific type
for(int i=0;i<openRooms.size();i++){
if(typeid(*aRoom)==typeid(*openRooms[i])) return *openRooms[i];
}
}
vector<Room> allOpenRooms(){//Return all open rooms
...
}
}
我对hotel.search()方法的实现感到困惑,我正在检查类型(我认为应该通过某种方式处理多态)。有没有更好的方法来设计这个系统,以便可以在不明确检查对象类型的情况下实现search和allOpenRooms方法?
答案 0 :(得分:3)
通过子类对象询问它们的类型并不是o-o设计的一个很好的例子。你真的需要你想要对所有房间做的事情而不知道每个房间的类型。例如,打印出房间的每日房间菜单(对于不同类型可能会有所不同)。 故意寻找子类对象的类型虽然没有错,但并不是很好的o-o风格。如果您只是想这样做,就像其他受访者所说的那样,只要拥有一套带有一系列属性的“房间”。
答案 1 :(得分:2)
您可以随时让房间携带它的真实类型,而不是比较对象类型:
enum RoomType
{
RegularRoom,
luxuryRoom
};
class Room{
public:
explicit Room(RoomType room_type) : room_type_(room_type) { }
virtual ~Room(){}
RoomType getRoomType() const { return room_type_; }
private:
RoomType room_type_; // carries room type
};
class regularRoom: public Room{
public:
regularRoom() : Room(RegularRoom){ }
};
Room search(Room *aRoom)
{
//Search room of a specific type
for(size_t i=0;i<openRooms.size();i++)
{
if (aRoom->getRoomType() == RegularRoom) // <<-- compare room type
{
// do something
}
}
};
答案 2 :(得分:1)
不同类型的房间有不同的行为吗?从 你给出的描述,这是不继承的情况 应该使用。每个房间都有一个属性,类型,哪个 就其最简单的形式而言,只是一个枚举。
答案 3 :(得分:0)
如果你真的想要检查一个房间与其他房间的类型相同,那么typeid()
就像其他方法一样好 - 而且肯定“更好”(从性能角度看,至少)调用虚方法。
另一个选择是根本没有单独的类,并将roomtype存储为成员变量(这当然是我设计它的方式,但这不是一个非常好的学习面向对象和继承的设计 - 你不要当基类满足您的所有需求时,我们可以继承。)
答案 4 :(得分:0)
最简单的方法是使用@billz建议的Room类型枚举。这种方法的问题在于,每次向系统添加新类型的Room时,都不要忘记为枚举添加值并使用它一次。您必须确保只使用枚举值一次,每个类一次。
但另一方面,如果层次结构的类型共享一个共同的行为,那么继承基础dessigns只有意义。换句话说,无论其类型如何,您都希望以相同的方式使用它们。 IMPO,OO /继承dessign不是更好的方法。
我做这类事情的怪胎和可扩展方式是通过类型列表。
通常,您对系统中的每种类型都有不同的搜索条件。并且,在许多情况下,对于不同类型的系统,此搜索的结果并不相同(搜索豪华房间和搜索普通房间的ssame不是,您可能有不同的搜索条件和/或想要不同的搜索结果数据)。
对于这个程序,系统有三个类型列表:一个包含数据类型,一个包含搜索条件类型,另一个包含搜索结果类型:
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.
}
引擎基于每个系统数据类型的存储一个向量。引擎使用存储该向量的向量。
我们不能有一个多态参考/指向不同类型的向量,所以我们使用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
)。
搜索引擎完全可扩展:要向系统添加新数据类型,您只能将类型添加到类型列表中。 搜索引擎的实施不会改变。
答案 5 :(得分:0)
房间等级
class Room{
public:
enum Type {
Regular,
Luxury,
Celebrity
};
Room(Type rt):roomType(rt), isOpen(true) { }
Type getRoomType() { return roomType; }
bool getRoomStatus() { return isOpen; }
void setRoomStatus(bool isOpen) { this->isOpen = isOpen; }
private:
Type roomType;
bool isOpen;
};
酒店等级
class Hotel{
std::map<Room::Type, std::vector<Room*>> openRooms;
//std::map<Room::Type, std::vector<Room*>> reservedRooms;
public:
void addRooms(Room &room) { openRooms[room.getRoomType()].push_back(&room); }
auto getOpenRooms() {
std::vector<Room*> allOpenRooms;
for(auto rt : openRooms)
for(auto r : rt.second)
allOpenRooms.push_back(r);
return allOpenRooms;
}
auto getOpenRoomsOfType(Room::Type rt) {
std::vector<Room*> OpenRooms;
for(auto r : openRooms[rt])
OpenRooms.push_back(r);
return OpenRooms;
}
int totalOpenRooms() {
int roomCount=0;
for(auto rt : openRooms)
roomCount += rt.second.size();
return roomCount;
}
};
Client UseCase:
Hotel Marigold;
Room RegularRoom1(Room::Regular);
Room RegularRoom2(Room::Regular);
Room LuxuryRoom(Room::Luxury);
Marigold.addRooms(RegularRoom1);
Marigold.addRooms(RegularRoom2);
Marigold.addRooms(LuxuryRoom);
auto allRooms = Marigold.getOpenRooms();
auto LRooms = Marigold.getOpenRoomsOfType(Room::Luxury);
auto RRooms = Marigold.getOpenRoomsOfType(Room::Regular);
auto CRooms = Marigold.getOpenRoomsOfType(Room::Celebrity);
cout << " TotalOpenRooms : " << allRooms.size()
<< "\n Luxury : " << LRooms.size()
<< "\n Regular : " << RRooms.size()
<< "\n Celebrity : " << CRooms.size()
<< endl;
TotalOpenRooms:4
奢侈品:2
常规:2
名人:0