Я предполагаю, что этот вопрос является более общим вопросом, связанным с моим вопросом, который можно найти по следующей ссылке:
Решение дизайна, включающего множественное наследование и составные классы в C ++
Я работаю с наборами данных, связанных с галактиками. Данные о каждой галактике будут содержать информацию о солнечных системах галактики, планетах каждой солнечной системы и лунах каждой планеты.
Составное требование:
Исходя из этого, я представляю, что мне нужно использовать составной класс, где я создаю класс для галактики, солнечных систем, планет и лун. Класс galaxy будет содержать контейнерные переменные-члены, которые содержат указатели на солнечные системы, планеты и луны в дополнение к соответствующим функциям получения и установки членов для этих контейнерных переменных. Солнечная система будет иметь аналогично спроектированные переменные-контейнеры-члены, содержащие указатели на содержащиеся в ней планеты и луны с геттерами и сеттерами и так далее с планетами.
Требование множественного наследования:
Типы входных данных, которые я использую для каждого из этих классов, не постоянны. Иногда у меня есть данные типа 1, а иногда — данные типа 2. Различные типы данных, которые у меня есть для любого данного проекта, могут меняться со временем. Чтобы не иметь чудовищно растущего класса галактик, который получает новые переменные и функции-члены для каждого типа данных, не доступных для каждого проекта, а также для всех старых (и, возможно, неиспользуемых в текущем проекте), я хочу разбить галактику solarSystem Классы планеты и Луны объединяются в связанные классы, каждый из которых специализируется на работе с определенным набором данных.
Таким образом, если я рассматриваю два из доступных мне наборов данных data1 и data2, я могу выбрать создание конкретных версий каждого класса для каждого набора данных.
Мне нравится этот подход, потому что, если у меня есть новый проект, который требует комбинации data1 и data2, я могу создать новые классы для классов galaxy, solarySystem, planet и moon, которые наследуются от обоих соответствующих типов ранее. Что-то вроде следующего.
class GalaxyOneTwo: public GalaxyOne, public GalaxyTwo { /* . . . */ };
class SolarSystemOneTwo: public SolarSystemTwo, public SolarSystemOne{ /* . . . */};
class PlanetOneTwo: public PlanetTwo, public PlanetOne{ /* . . . */ };
class MoonOneTwo: public MoonTwo, public MoonOne{ /* . . . .*/};
Это дает мне доступ ко всем проверяемым объектам и методам для работы с данными1 и данными2, и я могу дополнительно определить переменные и методы для работы с новыми свойствами, возникающими из комбинации данных1 и данных2.
Эта проблема:
Каждая из этих идей дизайна, составное требование и требование множественного наследования работали для меня сами по себе. Но я бы очень хотел использовать оба. Сложность использования обоих заключается в том, что те контейнерные переменные в GalaxyOne, которые предоставляют ему доступ к указателям SolarSystemOne, бесполезны для GalaxyOneTwo, поскольку типы указателей в SolarSystemOne не предоставляют доступ к функциям-членам SolarSystemOneTwo.
Возможные решения:
Шаблон Базовый класс-
Создайте базовый класс шаблона PrimitiveGalaxy, PrimitiveSolarSystem, PrimitivePlanet, PrimitiveMoon, который содержит все переменные контейнера и соответствующие методы получения и установки, от которых наследуются все остальные классы. Это подход, который я использовал в ссылке, размещенной выше, и его проблемы можно найти там.
Виртуальный базовый класс
Было предложено, чтобы я создал базовый класс, который содержит все общие функции для всех видов галактик, солнечных систем, планет и лун. Это будет включать в себя переменные контейнера для соответствующих указателей, а также их получателей и установщиков. Делая это, указатели можно использовать для указания на базовый класс каждого типа, и пока базовый тип содержит виртуальные функции для всех необходимых позже определенных функций, тогда я мог бы избежать приведения моих указателей к более сложным типам.
Меня беспокоит этот подход, поскольку он требует не только написания виртуальных функций в базовом классе для каждой новой функции в производных классах, но также и то, что не каждый производный класс будет определять каждую виртуальную функцию в базовом классе.
Пока что вышеупомянутое возможное решение — единственное, которое я в настоящее время понимаю достаточно хорошо, чтобы опубликовать.
Мне нужно решение, которое учитывает гибкий код, который не требует сложного синтаксиса и не требует каждой растущей и неуправляемой серии очень больших определений классов. Мои проекты регулярно включают новые виды данных, поэтому я хочу иметь возможность легко создавать новые парсеры для этих новых данных и легко включать данные в мои различные проекты с использованием классов Galaxy, SolarSystem, Planet и Moon.
Я смог решить свою проблему с помощью абстрактного базового класса. Абстрактный базовый класс содержит все мои переменные контейнера и соответствующие переменные getter и setter. Кроме того, абстрактный базовый класс определяет абстрактные методы для всех методов, которые будут использоваться в любом производном классе. Эти методы окружены директивами препроцессора, которые включают эти виртуальные функции только в том случае, если включен соответствующий заголовочный файл, который определяет указанные функции, что предотвращает дальнейшие абстрактные классы.
class Galaxy{
protected:
std::vector<SolarSystem*> solarSystems;
public:
void addSolarSystem(SolarSystem*);
#ifdef GALAXYONE
virtual void aGalaxyOneMethod()=0;
#endif
#ifdef GALAXYTWO
virtual void aGalaxyTwoMethod()=0;
#endif
#ifdef GALAXYONETWO
virtual void aGalaxyOneTwoMethod()=0;
#endif
enter code here
};
GalaxyOne.h
#define GALAXYONE
class GalaxyOne{
protected:
/*Private Variables for GalaxyOne*/
public:
void aGalaxyOneMethod(); //No longer abstract
};
GalaxyTwo.h
#define GALAXYTWO
class GalaxyTWO{
protected:
/*Private Variables for GalaxyTwo*/
public:
void aGalaxyTwoMethod(); //No longer abstract
};
GalaxyOneTwo.h
#define GALAXYONE
#define GALAXYTWO
#define GALAXYONETWO
class GalaxyOneTwo: virtual public GalaxyOne, virtual public GalaxyTwo{
protected:
/*Private Variables for GalaxyOneTwo*/
public:
void aGalaxyOneTwoMethod(); //No longer abstract
};
Эта схема позволяет создать объект типа GalaxyOneTwo, сохранить его в указателе Galaxy и при этом иметь доступ к соответствующим функциям. Аналогичная стратегия используется с SolarSystems, Planets and Moons
Других решений пока нет …