Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_ Лекция №10 - Более сложные формы композиции.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
68.53 Кб
Скачать

Композиция с кратностью многие-ко-многим

Отношение между объектами Book и Author более сложное, чем кажется на первый взгляд. С одной стороны, авторами одной и той же книги может быть несколько человек. С другой стороны - один автор мог участвовать в написании нескольких разных книг. Отношение с такой кратностью называют композицией многие-ко-многим. Это наиболее сложный вид композиции из всех возможных.

Отношение многие-ко-многим в большинстве случаев должно быть реализовано без ответственности за уничтожение. Такое отношение образует сложный граф связей между объектами, и понять используется объект где-либо еще достаточно сложно. Часто отношение многие-ко-многим представляет в предметной области некоторое логическое объединение нескольких сущностей в группу, нежели более сильное отношение владения, предполагающее ответственность за уничтожение. Обычно объекты, связанные отношением многие-ко-многим, создает и уничтожает какой-то третий объект, ответственный за хранение всех участвующих в такой модели объектов. В примере о книгах и авторах такую роль мог бы играть класс Library (библиотека).

Важной особенностью реализации такого вида композиции является обязательность использования форвардных деклараций в заголовочных файлах. Допустим, это ограничение нарушено, и программист использует директивы #include, чтобы связать определения классов друг с другом:

book.hpp

#include “author.hpp”

class Book

{

// ...

};

author.hpp

#include “book.hpp”

class Author

{

// ...

};

Попытка сборки проекта с таким подходом неизбежно приведет к зависанию либо фатальному сбою компилятора. Обрабатывая цепочку директив #include, препроцессор не сможет выбраться из бесконечной рекурсии, поскольку файлы включают друг друга.

Использование форвардных деклараций позволяет разорвать этот замкнутый круг. Директивы #include, свою очередь, размещают не в заголовочных файлах, а в файлах реализации классов. При этом, заголовочные файлы не могут использовать полные определения классов своих дочерних объектов. Соответственно, объявления всех полей и методов, как максимум, могут использовать эти классы только в виде указателя или ссылки, не обращаясь к содержимому. Работа с содержимым, при этом, в свободном доступе в CPP-файлов классов, поскольку они компилируются отдельно друг от друга и не образуют бесконечную рекурсию при компиляции.

Ниже представлен набросок примера с композицией многие-ко-многим, а полную версию можно посмотреть по ссылке в конце лекции.

author.hpp

#ifndef _AUTHOR_HPP_

#define _AUTHOR_HPP_

/*****************************************************************************/

#include <unordered_set>

#include <memory>

#include <functional>

#include <string>

/*****************************************************************************/

// Форвардное объявление

class Book;

/*****************************************************************************/

class Author

{

/*-----------------------------------------------------------------*/

public:

/*-----------------------------------------------------------------*/

// …

// Метод, возвращающий количество книг автора

int getBooksCount () const;

// Метод, выясняющий участвовал ли автор в написании указанной книги

bool hasBook ( Book const & _book ) const;

// Метод, регистрирующий очередную книгу автора

void addBook ( Book const & _book );

// Метод, отменяющий регистрацию одной из книг автора

void removeBook ( Book const & _book );

// Метод очистки набора книг

void clearBooks ();

// Метод обхода набора книг с применением указанного пользовательского действия

void forEachBook ( std::function< void ( Book const & ) > _action ) const;

/*-----------------------------------------------------------------*/

private:

/*-----------------------------------------------------------------*/

// …

// Множество книг автора

std::unordered_set< Book const * > m_books;

/*-----------------------------------------------------------------*/

};

/*****************************************************************************/

#endif // _AUTHOR_HPP_

author.cpp

#include “author.hpp”

#include “book.hpp”

// Реализация методов автора ...

library.hpp

#ifndef _LIBRARY_HPP_

#define _LIBRARY_HPP_

/*****************************************************************************/

#include <vector>

#include <memory>

/*****************************************************************************/

// Форвардные объявления

class Book;

class Author;

/*****************************************************************************/

class Library

{

/*-----------------------------------------------------------------*/

public:

/*-----------------------------------------------------------------*/

void addBook ( std::unique_ptr< Book > _book );

// ... другие полезные методы для работы с книгами

/*-----------------------------------------------------------------*/

void addAuthor ( std::unique_ptr< Author > _authors );

// ... другие полезные методы для работы с авторами

/*-----------------------------------------------------------------*/

private:

/*-----------------------------------------------------------------*/

// Книги библиотеки

std::vector< std::unique_ptr< Book > > m_books;

// Авторы книг библиотеки

std::vector< std::unique_ptr< Author > > m_authors;

/*-----------------------------------------------------------------*/

};

/*****************************************************************************/

#endif // _LIBRARY_HPP_

library.сpp

#include “library.hpp”

#include “author.hpp”

#include “book.hpp”

// Реализация методов библиотеки…