Design pattern of inventory management desktop application
-
Hi.
I am new in software development and I want to ask for your advice regarding the right design pattern and common approach for my project in Qt.
I'll try to keep everything simple in the question.
I am making an inventory management desktop application. I decided that the software should work as following:
There is a main window with a button get_btn, that when the user clicks, a label from the main window is updated and filled with data received from database.
So far I made a class clientAPI which is responsible for handling HTTP requests.
there is a web api written in fastapi - python.The current design pattern is as follows:
inside the clientAPI class, we have a function get_item which sends http get request.
when the responses is received, there is a signal from clinetAPI named signal_data emitting the data.
inside the mainWindow class we have the following:
we have a pointer of clientAPI named client as private member function in the mainWindow class. We have also a button get_btn, and a label as private member functions.
within the constructor of the mainWindow, we have two signal and slot connections. one connecting the clicked signal of get_btn to get_item function of client member function pointer.
In the other connection the signal signal_data from clinet is connected to the update_label slot of mainWindow.My question is that if this design pattern is the common or right approach?
(note that in this design pattern I have clinet as a pointer inside the mainWindow class as the member function)
Should the design pattern be in different way. For example to have these two signal and slot connections in the main.cpp file and not inside the mainWindow.cpp. In other word, rather than having a client pointer as member function of the mainWindow, I make the two signal and slot connections inside the main.cpp.Below I provide the code of clientAPI class which is common across the two appraoches and the code of two appraoches.
clientapi.cpp code:
#include "clientapi.h" clientAPI::clientAPI(QObject *parent): QObject{parent} { manager = new QNetworkAccessManager(this); QObject::connect( manager, &QNetworkAccessManager::finished, this, &clientAPI::reply_finished); } clientAPI::~clientAPI() { delete(manager); qInfo() << "manager pointer was deleted."; qInfo() << this << "deconstructed"; } void clientAPI::get_items() { manager->get(QNetworkRequest(QUrl("http://127.0.0.1:8000/items/"))); qInfo() << "get request was sent"; } void clientAPI::reply_finished(QNetworkReply* _reply) { QByteArray responses = _reply->readAll(); QJsonDocument JsonDoc = QJsonDocument::fromJson(responses); QJsonArray JsonArray = JsonDoc.array(); QString data = JsonArray.at(1)["title"].toString(); emit data_received(data); _reply->deleteLater(); }appraoch one :
code of MainWindow.cpp
#include "mainwindow.h" MainWindow::MainWindow(clientAPI *_client, QWidget *parent): QMainWindow(parent), client(_client) { get_btn = new QPushButton(this); test_label = new QLabel(this); get_btn->setGeometry(200,150, 80,40); get_btn->setText("register"); test_label->setGeometry(300,150, 80,40); test_label->setText("..."); test_label->setFrameStyle(QFrame::WinPanel | QFrame::Plain); //should I use test_label instead of this?!! && what is private slots, is public better. connect(get_btn, &QPushButton::clicked, client, &clientAPI::get_items); connect(client, &clientAPI::data_received, this, &MainWindow::update_label); } MainWindow::~MainWindow() = default; void MainWindow::update_label(const QString _data) { test_label->setText(_data); }main.cpp in appraoch 1 is :
int main(int argc, char *argv[]) { QApplication a(argc, argv); clientAPI* client = new clientAPI(); MainWindow w(client); w.show(); return a.exec(); }In appraoch 2, the MainWindow is as follow:
#include "mainwindow.h" MainWindow::MainWindow(QWidget *parent): QMainWindow(parent) { get_btn = new QPushButton(this); test_label = new QLabel(this); get_btn->setGeometry(200,150, 80,40); get_btn->setText("register"); test_label->setGeometry(300,150, 80,40); test_label->setText("..."); test_label->setFrameStyle(QFrame::WinPanel | QFrame::Plain); } MainWindow::~MainWindow() = default; void MainWindow::update_label(const QString _data) { test_label->setText(_data); } QLabel *MainWindow::getTest_label() const { return test_label; } void MainWindow::setTest_label(QLabel *newTest_label) { test_label = newTest_label; } QPushButton *MainWindow::getGet_btn() const { return get_btn; } void MainWindow::setGet_btn(QPushButton *newGet_btn) { get_btn = newGet_btn; }and the main.cpp in the appraoch 2 is:
int main(int argc, char *argv[]) { QApplication a(argc, argv); clientAPI client(); MainWindow w(); QObject::connect(w.getGet_btn(), &QPushButton::clicked, &clinet, &clientAPI::get_items); QObject::connect(&client, &clientAPI::data_received, w.getTest_label(), &MainWindow::update_label); w.show(); return a.exec(); }Tanks a lot for your guide.
both of these approaches are working as I tested them, but I want your advice for designing a software, what approach you would choose? which one is more common and the right approach?
sorry of the question is long, let me know if I should make it shorter. -
Hi,
Approach 1 is correct.
MainWindow does not need to expose its internal structure in this case. In fact, it is pretty rare that an application main window exposes its internal state like for your solution #2.
One other good point is creating your API client outside of MainWindow. This allows scenarios such as passing a fake API client for testing purpose if for any reason you cannot contact/spin up your backend server. This does not seem to be the case here but still it can be useful. -
I imanipourmeysa has marked this topic as solved
-
common practice is to designate class members with m_ otherwise you invoke easily scope crashes. otherwise both approaches are just matter of taste
@sales99 said in Design pattern of inventory management desktop application:
common practice is to designate class members with m_ otherwise you invoke easily scope crashes. otherwise both approaches are just matter of taste
I disagree on the "just matter of taste". Each has different practical implications which go beyond taste. On the other hand, the common practice you point is a matter of taste.
-
common practice is to designate class members with m_ otherwise you invoke easily scope crashes. otherwise both approaches are just matter of taste