2013-06-16 4 views
3

Я практикую объектно-ориентированный дизайн для предстоящего собеседования. Мой вопрос о дизайне системы бронирования отелей: - Система должна быть в состоянии вернуть открытую комнату определенного типа или вернуть все открытые комнаты в отеле. - Есть много типов номеров в отеле, таких как регулярные, роскошные, знаменитости и так далее.Объектно-ориентированный дизайн для системы бронирования отелей

До сих пор я придумал следующие классы:

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() метод, где я проверяю тип (который я считаю, должны быть обработаны полиморфизма каким-то образом). Есть ли лучший способ разработки этой системы, чтобы методы поиска и allOpenRooms могли быть реализованы без явной проверки типа объектов?

+0

Тест 'TypeId (aRoom) == TypeID (openRooms [я])' всегда будет иметь успех, потому что 'aRoom' является' номер * 'и' openRooms' содержит элементы типа 'Room *'. Поэтому вы можете просто заменить 'if (typeid (aRoom) == typeid (openRooms [i]))' с 'if (true)'. – Oswald

+0

@ Освальд: Правда? Я думал, что 'tyepid' разрешит значение, соответствующее ACTUAL типу аргумента. –

+0

Да, да. 'Room *' * - * действительный тип. Если вы хотите проверить, указывают ли в ваших указателях разные типы времени выполнения, вы должны проверить, является ли typeid (* aRoom) == typeid (* openRooms [i]) ' – Oswald

ответ

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 
     } 
    } 
}; 
+1

Это определенно возможно, но это не сильно отличается от использования typeid(). Здесь тип замаскирован как член данных класса. Мне интересно узнать альтернативные шаблоны проектирования, которые могут полностью искоренить сравнение типа комнаты, если это возможно. – Mishra

+1

Это, безусловно, «лучше» с точки зрения дизайна. – SChepurin

+0

Не обязательно, поскольку наличие подклассов позволяет добавлять атрибуты определенных комнат. Например, для роскошного номера может быть бильярдный стол в качестве одного из его атрибутов. –

0

Если вы действительно хотите, чтобы проверить, что номер одного и того же типа, как и какой-либо другой комнате, то typeid() так же хорош, как и любой другой метод - и это, безусловно, «лучше» (с точки зрения производительности, по крайней мере) для вызова виртуального метода.

Другой вариант состоит в том, чтобы не иметь отдельных классов вообще и хранить тип номера как переменную-член (и это, безусловно, то, как я его создавал, но это не очень хороший дизайн для изучения ориентации объекта и наследования - вы не наследуйте, когда базовый класс удовлетворяет все ваши потребности).

+0

Я считал сохранение переменной-члена для типа комнаты, но постановил, что из-за того, что меня могут попросить написать новые методы, такие как raisePrice(), который сильно зависит от типа комнаты. Выполнение этого в базовом классе будет стимулировать процедурный код. – Mishra

+0

@ user2490334 Даже в таких случаях предпочтительнее сохранить тип в переменной-члене. Однако вы можете подумать о том, чтобы сделать переменную-член переменной полиморфной. Хотя даже здесь, я думаю, оставляя тип простой переменной, а затем используя какую-то полиморфную стратегию для различных специальных функций, будет более чистым. –

1

Разные типы номеров имеют по-разному поведение? От описание, которое вы даете, это не случай, когда необходимо наследовать . Каждая комната просто имеет атрибут, тип, который в своей простейшей форме просто перечисляет.

3

Пройдя через объекты подкласса, спрашивая, какой тип они на самом деле не является хорошей иллюстрацией дизайна o-o. Вам действительно нужно что-то, что вы хотите сделать во всех комнатах, не зная, какой тип каждого. Например, распечатайте ежедневное меню комнаты для комнаты (которое может отличаться для разных типов). Умышленно ищет тип объекта подкласса, хотя он и не ошибается, не является большим стилем o-o. Если вы просто хотите это сделать, как сказали другие респонденты, просто «комнаты» с набором свойств.

0

Простейший способ состоит в том, чтобы перечисление типа комнаты, как @billz, предлагает вам. Проблема с этим заключается в том, что вы не должны забывать добавлять значение в перечисление и использовать его один раз при каждом добавлении нового типа комнаты в систему. Вы должны быть уверены, что используете значения перечисления только один раз, один раз за класс.

Но, с другой стороны, Наследование bassed dessigns имеет смысл только в том случае, если типы иерархии имеют общее поведение. Другими словами, вы хотите использовать их одинаково, независимо от его типа. IMPO, OO/inheritance dessign - не лучший способ сделать это.

Урод и масштабируемый способ Я делаю такие вещи через typelists.

Обычно у вас есть разные критерии поиска для каждого типа в вашей системе.И, во многих случаях, результаты этого поиска не одинаковы для разных типов вашей системы (не так ли ssame искать комнату роскоши и искать обычную комнату, у вас могут быть разные критерии поиска и/или нужен другой поиск данные результатов).

Для этого prupose, система имеет три списков типов: один, содержащие тип данных, один содержащие типы критериев поиска, и один, содержащие тип результатов поиска:

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]; 
} 

Преимущества этого dessign является:

  • Поисковой система использует точно такой же интерфейс для различных запросов (поисковые запросы с типами данных разных систем как цель). И процесс «связывания» типа данных с его соответствующими критериями поиска и результатами выполняется во время компиляции.

  • Этот интерфейс Тип сейфа: Обращение к SearchEngine::search() возвращает тип результатов, основанный только на критериях поиска. Ошибки результатов присваивания обнаруживаются во время компиляции. Например: NormalRoomResults = engine.search(LuxuryRoomCriteria()) генерирует ошибку компиляции (engine.search<LuxuryRoomCriteria> возвращает LuxuryRoomResults).

  • Поисковая система полностью масштабируемая: Чтобы добавить новый тип данных в систему, вы должны перейти только к добавлению типов в списки типов. Реализация поисковой системы не изменяется.

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; 
    } 

}; 

Клиент 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
Luxury: 2
Regular: 2
Celebrity: 0

Смежные вопросы