Я работаю над Qt-основанным CAD-приложением, и я пытаюсь понять архитектуру приложения. Приложение может загружать несколько проектов с планами, разделами и т.д. И показывать эти чертежи в выделенных представлениях. На каждый проект и глобальные конфигурации.
Приложение представлено глобальным объектом, полученным из QApplication
:
class CADApplication Q_DECL_FINAL: public QApplication {
Q_OBJECT
public:
explicit CADApplication(int &args, char **argv);
virtual ~CADApplication();
...
ProjectManager* projectManager() const;
ConfigManager* configManager() const;
UndoManager* undoManager() const;
protected:
const QScopedPointer<ProjectManager> m_projectManager;
const QScopedPointer<ConfigManager> m_configManager;
...
};
"Менеджеры" создаются в конструкторе CADApplication
. Они отвечают за функциональность, связанную с загруженными проектами (ProjectManager
), глобальными параметрами конфигурации (ConfigManager
) и т.д.
Существуют также виды проектов, диалоговые окна параметров конфигурации и другие объекты, которым может потребоваться доступ к "менеджерам".
Чтобы получить текущий проект, SettingsDialog
должен:
#include "CADApplication.h"
#include "ProjectManager.h"
...
SettingsDialog::SettingsDialog(QWidget *parent)
: QDialog(parent)
{
...
Project* project = qApp->projectManager()->currentProject();
...
}
Что мне нравится в целом, так это то, что он следует парадигме RAII. "Менеджеры" создаются и уничтожаются при создании/уничтожении аппликации.
Мне не нравится, что он не подвержен циклическим ссылкам, и что мне нужно включить "CADApplication.h" из любого исходного файла, где требуется экземпляр любого из "менеджеров". Это похоже на CADApplication
объект CADApplication
используется как некий глобальный "держатель" этих "менеджеров".
Я провел некоторое исследование. Похоже, есть и несколько других подходов, которые подразумевают использование синглетонов. OpenToonz использует TProjectManager
:
class DVAPI TProjectManager {
...
public:
static TProjectManager *instance();
...
};
TProjectManager* TProjectManager::instance() {
static TProjectManager _instance;
return &_instance;
}
В каждом файле, где им необходимо получить доступ к менеджеру проекта:
#include "toonz/tproject.h"
...
TProjectManager *pm = TProjectManager::instance();
TProjectP sceneProject = pm->loadSceneProject(filePath);
Из вашего опыта, какой из этих подходов я должен придерживаться, чтобы добиться хорошей архитектуры и сделать склонность к ошибкам приложения и упростить модульное тестирование? Может быть, есть и другие парадигмы?