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


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

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



 

Отношение между объектами 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”

 

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

 

Полные примеры из лекции

 

https://github.com/zaychenko-sergei/oop-samples/tree/master/lec10

 

Выводы

 

В данной лекции были показаны типичные ошибки начинающих программистов при реализации отношения композиции между объектами на основе STL-контейнеров, а также выданы подробные рекомендации по удачному стилю реализации родительских классов. Рассматривались возможные варианты улучшения инкапсуляции при помощи контейнерных итераторов, а также средства std::function.

 

Позднее было введено понятие умных указателей, в частности, std::unique_ptr, и рассматривались способы их применения для упрощения реализации одиночной и множественной композиции с ответственностью за уничтожение дочерних объектов.

 

В завершении речь шла о вариантах оптимизации быстродействия композиции объектов за счет использования стандартных реализаций отображений и множеств, а также особенности применения композиции с кратностью многие-ко-многим, в частности, обязательность форвардных деклараций для разрыва компиляционных зависимостей.



Поделиться:


Последнее изменение этой страницы: 2017-02-17; просмотров: 114; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.217.208.72 (0.015 с.)