Я хочу отделить этот проект Qt Calculator Example от модуля GUI и модуля Business Logic. Каков наилучший способ сделать это, и лучше ли писать логический модуль только на C++, чтобы я мог использовать его и в других IDE?Модули Qt GUI и Business Logic
ответ
Лучшая практика или дизайн в вашем случае GRASP Controller модель.
В вашем случае - это означает, что вы должны полностью отделить класс Calculator
от материалов Qt (например, QWidget
).
Итак - если вам нужно что-то представить от Calculator
к виджетам Qt - создайте и используйте интерфейс, например CalculatorPresentationInterface
.
Чтобы получить некоторые обратные вызовы, события GUI для калькулятора - сделать слоты обратных вызовов или интерфейс CalculatorConrollerInterface
.
Итак, ваш калькулятор будет реализовывать CalculatorConrollerInterface
, чтобы иметь возможность получать события из графического интерфейса.
Используйте dependency injection pattern, чтобы ввести Calculator
через CalculatorConrollerInterface
в ваш GUI.
Ваш GUI должен реализовать (или вы можете использовать adapter pattern) CalculatorPresentationInterface
и вы должны вводить свой графический интерфейс для вашего реального Calculator
класса через CalculatorPresentationInterface
.
Таким образом, оба слоя уровня бизнес-логики (калькулятор) и логики представления (Qt GUI) будут отделены друг от друга, и вы можете легко обменивать оба слоя.
Пример (каждый класс в отдельном файле).
Интерфейсы:
class CalculatorConrollerInterface
{
public:
virtual void onAdd() = 0;
virtual void onCurrentNumberChange(int number) = 0;
};
class CalculatorPresentationInterface
{
public:
virtual void showResult(int result) = 0;
};
Калькулятор - не должен иметь никакого отношения к Qt:
class Calculator : public CalculatorConrollerInterface
{
public:
Calculator(CalculatorPresentationInterface& presentation)
: presentation(presentation)
{}
virtual void onAdd() override
{
// no idea this is correct - just example
previousNumber = previousNumber + currentNumber;
currentNumber = 0;
presentation.showResult(previousNumber);
}
void onCurrentNumberChange(int number) override
{
currentNumber = number;
}
private:
CalculatorPresentationInterface& presentation;
// all stuff necessary to calculate
int previousNumber;
int currentNumber;
};
Qt презентацию для калькулятора:
class QtCalculatorPresentation : public CalculatorPresentationInterface
{
public:
void setController(CalculatorConrollerInterface& controller)
{
this->controller = &controller;
}
void showResult(int result) override;
private:
CalculatorConrollerInterface* controller;
// plus all Qt widgets necessary
// and they shall forward any event to controller
};
И ваш главный:
#include "Calculator.hpp"
#include "QtCalculatorPresentation .hpp"
int main()
{
// dependency injections
QtCalculatorPresentation qtPresentation;
Calculator calculator(qtPresentation);
qtPresentation.setController(calculator);
qtPresentation.exec();
}
Учитывая, что Qt представляет собой платформу разработки приложений, одержимость сохранением бизнес-логики, не использующей Qt, является IMHO необоснованной. Вы теряете сигналы/слоты, интроспекцию, потоки, сообщения (события) и т. Д. Для бизнес-объекта калькулятора вы действительно должны использовать конечный автомат, например QStateMachine, поскольку эти вещи * заведомо * трудно получить правильный код, а пример кода с неправильной функциональностью почти вездесущий в Интернете именно потому, что люди не утруждают себя указателем состояния для системы. –
С Qt вам не нужна смирительная рубашка фиксированного класса интерфейса с виртуальными методами, определяющими интерфейс. Очень просто подключить гетерогенных клиентов и поставщиков, использующих сигналы/слоты; функторы могут использоваться для адаптации несоответствующего поставщика к потребностям клиента и т. д. Насколько мне известно, классический шаблон «интерфейс как абстрактный класс» очень негибкий и очень сложный в использовании (или отсутствует!) при взаимодействии с сторонним кодом.Обычно интерфейсы не задаются как абстрактные классы, а конкретные классы только с одной реализацией, определяющей API. –
@KubaOber - 1) В C++ 14 + boost у вас есть все, что вы можете себе представить, Qt когда-либо обеспечит. 2) Если вам действительно нужны таймеры qt state-machine/qt и т. Д. - тогда точно так же вы можете создать его в Qt - и подключиться через интерфейс C++ к вашему «бизнес-контроллеру» - чтобы он мог использовать абстрактные таймеры и т. Д. ... – PiotrNycz
Шаблон MVC в сочетании с правилом, согласно которому модель не должна использовать типы Qt, приведет вас в правильном направлении. –