В моей предыдущей работе большая часть обработки программы основывалась на постоянных данных, хранящихся в БД.
Таким образом, модель данных БД опередила структуры данных программ во время выполнения. Таким образом, нам было очень удобно использовать значения первичных ключей в качестве ссылок на другие объекты.
Например :
1 — Учитывая, что у нас есть компания, которая продает товары, такие как книги, клиентам через Интернет. У нас есть три класса: книга, заказ и клиент.
Класс Book содержит различную информацию о книге, а также уникальный идентификатор, такой как номер ISBN.
Класс Customer содержит все данные (и, как правило, намного больше), которые компания должна знать, чтобы отправить книги своим клиентам, например адрес электронной почты. Объекты клиентов также имеют уникальный постоянный идентификатор, который их идентифицирует.
Таким образом, класс Order содержит две реляционные ссылки int isbn;
(идентификатор книги) и
int customer_id;
В этом примере методы класса заказа не нуждаются в доступе к данным клиента или данным книги, поэтому класс заказа не должен зависеть от них.
2 — Если мы сейчас рассмотрим другой класс, который используется для написания и отправки подтверждения заказа по электронной почте:
class OrderMailer
{
// Customer index
std::map<int, Customer *> customers;
...
// we have a function that send email with low level parameters
void sendEmail(const std::string& mailAddress, const std::string& body);
// and we have another method that simply sends the email for a given order
void sendEmail(const Order& order);
};
SendEmail (постоянный заказ& Метод order) должен получить адрес электронной почты клиента, поэтому ему нужно получить объект по его идентификатору.
Вот почему у нас есть карта, и тогда адрес будет доступен так
const std::string& target = customers[order.customer_id]->emailAddress; // not found test omitted for reading.
Это идея.
Часть вопроса:
Я использовал этот способ ссылки в течение нескольких лет, не спрашивая себя: «Это действительно хорошая идея», потому что:
идентификаторы объекта / записи были способом идентификации объекта в компании
такие постоянные идентификаторы, где они используются везде (код, журналы, обсуждение с другими ИТ-специалистами)
структуры данных во время выполнения всегда отражали модель данных БД (это может быть не вполне обоснованным аргументом, но это было очень полезно при переключении между миром БД и миром выполнения (c ++, python, js))
Я больше не в этой компании, но я придерживался такого способа программирования, когда имел дело с постоянными записями, и я не уверен, что все делаю правильно.
Что мы наделали?
Мы использовали логический способ ссылки на объекты вместо того, чтобы использовать язык: указатели или ссылки на c ++. Это звучит очень плохо для меня, когда так говорят.
Вот список плюсов и минусов использования этого «метода» с моей точки зрения:
Плюсы:
Минусы:
Как я уже сказал, я не уверен, что это хорошая вещь, чтобы использовать логические / реляционные ссылки. Есть ли какие-то правила, которые можно применять, чтобы решить, использовать этот подход или нет?
Я буду рад узнать ваше мнение по этому поводу.
Ну, вот как бы я это сделал. Так как это C ++ и если мы используем стандартные контейнеры. Почему бы не использовать итераторы. Я добавил немного печатного кода для удобства тестирования.
Если вам нужна стабильность итератора, измените вектор на список. Для лучшего поиска операций посмотрите на boost::multi_index
http://www.boost.org/doc/libs/1_53_0/libs/multi_index/doc/index.html. boost::multi_index
также хорошо использовать, если важна стабильность итератора.
Вероятно, было бы также легко использовать boost::serialization
если вы хотите записать свои структуры данных в файл.
#include <string>
#include <iostream>
#include <vector>
struct Streamable
{
virtual ~Streamable() = default;
virtual std::ostream& print(std::ostream& ost) const = 0;
};
std::ostream& operator<<(std::ostream& ost, const Streamable& str)
{
return str.print(ost);
}
struct Customer : public Streamable
{
std::string name_m;
Customer(const std::string& name_):name_m(name_)
{}
std::ostream& print(std::ostream& ost) const
{
return ost<<name_m;
}
};
struct CustomerColl : public std::vector<Customer>,
public Streamable
{
CustomerColl(std::initializer_list<Customer> customers)
:std::vector<Customer>(customers)
{}
std::ostream& print(std::ostream& ost) const
{
ost<<"Customers:"<<std::endl;
for(const auto& c: *this)
{
ost<<c<<std::endl;
}
return ost;
}
};
struct Book : public Streamable
{
std::string title_m;
unsigned int isbn_m;
Book(const std::string& title_, const unsigned int& isbn_)
:title_m(title_),isbn_m(isbn_)
{}
std::ostream& print(std::ostream& ost) const
{
return ost<<title_m<<":{"<<isbn_m<<"}";
}
};
struct BookColl : public std::vector<Book>,
public Streamable
{
BookColl(std::initializer_list<Book> books)
:std::vector<Book>(books)
{}
std::ostream& print(std::ostream& ost) const
{
ost<<"Books:"<<std::endl;
for(const auto& b: *this)
{
ost<<b<<std::endl;
}
return ost;
}
};
struct Order : public Streamable
{
BookColl::const_iterator book_m;
CustomerColl::const_iterator customer_m;
Order(const BookColl::const_iterator& book_,
const CustomerColl::const_iterator& customer_)
:book_m(book_),customer_m(customer_)
{}
std::ostream& print(std::ostream& ost) const
{
return ost<<"["<<*customer_m<<"->"<<*book_m<<"]";
}
};
struct OrderColl : public std::vector<Order>,
public Streamable
{
OrderColl(std::initializer_list<Order> orders)
:std::vector<Order>(orders)
{}
std::ostream& print(std::ostream& ost) const
{
ost<<"Orders:"<<std::endl;
for(const auto& o: *this)
{
ost<<o<<std::endl;
}
return ost;
}
};
int main()
{
CustomerColl customers{{"Anna"},{"David"},{"Lisa"}};
BookColl books{{"C++",123},{"Java",234},{"Lisp",345}};
OrderColl orders{{books.begin(),customers.begin()},{books.begin(),customers.begin()+1},{books.end()-1,customers.begin()+2}};
std::cout<<customers<<std::endl;
std::cout<<books<<std::endl;
std::cout<<orders<<std::endl;
return 0;
}
Других решений пока нет …