I want to separate this project Qt Calculator Example into the GUI module and the Business Logic module. What is the best way to do so and is it better to write the logic module in only C++, so that I can use it in other IDE's as well?
-
1MVC pattern, combined with the rule that a model should NOT use Qt types, will take you in the right direction. – Mar 09 '16 at 13:59
1 Answers
The best practice or design pattern in your case is GRASP Controller pattern.
In your case - it means you shall separate class Calculator
from Qt stuff (like QWidget
) completely.
So - if you need to present something from Calculator
to Qt widgets - create and use interface like CalculatorPresentationInterface
.
To get some callbacks, GUI events to calculator - make callbacks slots, or interface CalculatorConrollerInterface
.
So - your calculator will implement CalculatorConrollerInterface
to be able to receive events from GUI.
Use dependency injection pattern to inject your Calculator
via CalculatorConrollerInterface
to your GUI.
Your GUI shall implement (or you can use adapter pattern) CalculatorPresentationInterface
and you shall inject your GUI via CalculatorPresentationInterface
to your real Calculator
class.
In this way both layers Business logic layer (Calculator) and presentation logic (Qt GUI) will be separated from each other and you can easily exchange both layers.
An example (each class in separate file).
Interfaces:
class CalculatorConrollerInterface
{
public:
virtual void onAdd() = 0;
virtual void onCurrentNumberChange(int number) = 0;
};
class CalculatorPresentationInterface
{
public:
virtual void showResult(int result) = 0;
};
The calculator - shall not have any connection to 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 presentation for calculator:
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
};
And your main:
#include "Calculator.hpp"
#include "QtCalculatorPresentation .hpp"
int main()
{
// dependency injections
QtCalculatorPresentation qtPresentation;
Calculator calculator(qtPresentation);
qtPresentation.setController(calculator);
qtPresentation.exec();
}

- 23,099
- 7
- 66
- 112
-
1Given that Qt is an application development framework, the obsession with keeping business logic not using Qt is IMHO unwarranted. You're losing out on signals/slots, introspection, threading, messaging (events), etc... For a calculator business object, you really should be using a state machine, like QStateMachine, since these things are *notoriously* hard to get right, and example code with wrong functionality is almost omnipresent on the web precisely because people don't bother to specify the statechart for the system. – Kuba hasn't forgotten Monica Mar 09 '16 at 15:01
-
With Qt you don't need the straitjacket of a fixed interface class with virtual methods specifying the interface. It's very easy to connect heterogenous clients and providers using signals/slots; functors can be used to adapt a non-conforming provider to the needs of a client, etc. As far as I'm concerned, the classical "interface as an abstract class" pattern is highly inflexible and very hard to use (or absent!) when you interface with 3rd party code. Usually the interfaces are not given as abstract classes, but concrete classes with just one implementation defining the API. – Kuba hasn't forgotten Monica Mar 09 '16 at 15:05
-
@KubaOber - 1) In C++14+boost you have everything you can imagine Qt will ever provide. 2) If you really need some qt state-machine/qt timers etc.. - then in the very same way you can create one in Qt - and connect via C++ interface to you "business Controller" - so it can use abstract timers etc... – PiotrNycz Mar 09 '16 at 18:28
-
@KubaOber 3) I always do this way - it is very easy to use - just a matter of taste - and OP wanted to try this way 4) singnal/slots use in your Qt world classes - no problem and even advisable - but out of it - only C++ interfaces to connect to your business logic - alternatively use `std::function` or `boost::signal`... – PiotrNycz Mar 09 '16 at 18:28
-
"In C++14+boost you have everything you can imagine Qt will ever provide" Not introspection :) – Kuba hasn't forgotten Monica Mar 09 '16 at 18:31
-
@KubaOber AFAIK - works ongoing - http://stackoverflow.com/questions/22206420/compile-time-reflection-in-c1z - 2017 is coming... Anyway - I do not see much usage of introspection for business logic... – PiotrNycz Mar 09 '16 at 18:37