Как я могу поддерживать хорошую инкапсуляцию данных в этой ситуации?
Я новичок в C++, я пытаюсь сделать очень простую программу CRUD.
В этом примере клиент купил много вещей в магазине, и у этого магазина есть информация о вещах, которые купил этот клиент. Я назвал это Inventory
,
Магазин, хочет распечатать Report
для каждого клиента. В приведенном ниже коде основной только один инвентарь, только для примера.
Проблема в том, что когда я хочу распечатать отчет, я должен получить данные от клиента, но без потери инкапсуляции. Я имею в виду, я хочу, чтобы ни один класс не мог изменить содержимое инвентаря.
Я пытаюсь преобразовать карту в вектор (мне нужно что-то для сортировки данных) и передать этот вектор (динамически выделенный). Я выделяю этот вектор в классе Inventory
но кто удаляет это класс Report
Это не правильный способ сделать что-то, но я не знаю, как передать эту информацию, не делая так.
В любом случае, класс отчета может получить указатель на книгу и использовать ее set
функция или указать на другую книгу. Опять же, я не знаю, как это сделать правильно.
Может ли кто-нибудь дать мне совет, что мне делать в этом случае?
Благодарю.
Извините за длинный код.
Главный:
int main(void)
{
Inventory i;
Report r(i);
i.addBook("Foo Bar I");
i.addBook("Foo Bar II");
r.generateReport();
return 0;
}
Отчет о классе в.h:
class Report
{
private:
Inventory* i;
public:
Report(Inventory& i);
void generateReport();
};
Отчет о классе в cpp:
Report::Report(Inventory& i)
{
this->i = &i;
}
void Report::generateReport()
{
ofstream out ("Report.txt");
out << "Books: " << endl;
vector<pair<int, Book *>> * b = i->getBooks();
for(pair<int, Book *> p : *b)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
delete b;
out.close();
}
Инвентаризация классов в.h:
class Inventory
{
private:
map<int, Book *> books;
public:
void addBook(int code, const string& name);
vector<pair<int, Book *>> * getBooks();
};
Инвентаризация классов в.cpp:
void Inventory::addBook(int code, const string& name)
{
books.insert(pair<int, Book *>(code, new Book(name)));
}
vector<pair<int, Book *>> * Inventory::getBooks()
{
return new vector<pair<int, Book *>>(books.begin(), books.end());
}
2 ответа
Ваш класс инвентаря должен иметь этот интерфейс:
class Inventory
{
private:
map<int, Book *> books;
public:
using const_iterator = std::map<int, Book*>::const_iterator;
void addBook(int code, const string& name);
const_iterator begin() const { return books.begin(); }
const_iterator end() const { return books.end(); }
};
нет причин создавать копию карты в вектор! это неэффективно, а не причина для этого.
Переписать generateReport
работать с этим интерфейсом следующим образом:
for(const auto &p : *i)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
С вашим подходом уловка, чтобы преобразовать map
в vector
вводит только дополнительную зависимость: отчет должен знать о внутренностях инвентаризации.
Я бы порекомендовал пойти по пути шаблона проектирования vistitor: цель состоит в том, чтобы отделить алгоритм (создание отчета, посетителя) от исследуемой структуры данных (инвентарь и его элементы).
class Item { ...}; // Your object structure
class Book : public Item { ... };
class Inventory { ...};
class Visitor { ... }; // Algorithms that shall be executed on structure
class Report : public Visitor {...};
Это также может упростить реализацию вашего инвентаря, так как вам больше не нужно предусматривать разные контейнеры для элементов различного типа (при условии, что все они наследуются от некоторого общего базового класса элементов).
Theres много литературы по этому шаблону. Я рекомендую вам разработать шаблоны, элементы многоразового объектно-ориентированного программного обеспечения от Gamma & al: это оригинальный учебник, и угадайте, что, демо-код показывает инвентарь с посетителем ценообразования;-)
Вот наивный онлайн-пример, основанный на вашей проблеме, чтобы проиллюстрировать, как это может работать.