Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Probleme sutitrare pe apple tv 4k

Un sfat va rog la un land rover f...

Pc pre-configured.

Exindere monitor pe un TV
 Lacul Sf.Ana

Alternativa pentru Bitlocker

Becuri LED fiabile

Afisare info website dreapta la c...
 Zgomote cuidate PC. Sa fie de la ...

Avast Safezone Browser - s-a inst...

Problema transfer fișiere pe...

Cu cat poate fi mai scumpa o casa...
 Cablu prelungire videoproiector B...

A Quiet Place - 2018

Providerii meteo Android?

Minciuni si manipulare enel
 

Baze de date embedded – exercitiu practic (pentru programatori mid-level)

- - - - -
  • Please log in to reply
91 replies to this topic

#1
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013

Vizualizare mesajjegmihai, pe 06 septembrie 2018 - 22:03, a scris:

OK, sper să reușesc să o fac cât mai curând.
Am revenit.

Am decis să încerc următorul challenge propus de @dani.user:

Vizualizare mesajdani.user, pe 02 iulie 2018 - 20:09, a scris:

Incearca ceva tipic studentesc: o agenda telefonica, dar mai progresiva, cu sqlite ca baza de date, eventual si interfata grafica.

Să luăm, spre exemplu, metoda prin care creez tabelul principal:

auto PhoneBook::createDatabaseMainTable() -> void
{
static QString const createTableQuery{
	 "CREATE TABLE contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, lastname "
	 "TEXT, firstname TEXT, phonenumber TEXT);"
};
static QString const errorMessage{ "Could now create the table." };
QSqlQuery query;
if (!query.exec(createTableQuery)) {
	 QMessageBox::critical(this, errorMessage, query.lastError().text());
	 std::exit(EXIT_FAILURE);
}
}


Având în vedere că întreaga aplicație se bazează pe acel tabel, nu cred că mai avea sens să o mai las să ruleze. Nu știu dacă abordarea mea este una corectă.

Voi atașa și link-ul de GitHub. Butonul de Delete este dezactivat deocamdată (vezi Issues), altfel restul funcțiilor funcționează. Aș aprecia dacă aș primi și ceva review pe cod.

https://github.com/ISilviu/PhoneBook

#2
dani.user

dani.user

    Guru Member

  • Grup: Moderators
  • Posts: 24,018
  • Înscris: 24.02.2007
E de inteles ca trebuie sa opresti executia, nemaiavand ce sa faci.

Dar sa mai separa logica (inclusiv accesul la baza de date) de interfata.

#3
CrocodiluMereuVesel

CrocodiluMereuVesel

    Junior

  • Grup: Members
  • Posts: 361
  • Înscris: 23.07.2018

View Postjegmihai, on 07 septembrie 2018 - 22:27, said:

Am revenit.

Am decis să încerc următorul challenge propus de @dani.user:


Să luăm, spre exemplu, metoda prin care creez tabelul principal:

auto PhoneBook::createDatabaseMainTable() -> void
{
static QString const createTableQuery{
	 "CREATE TABLE contacts (id INTEGER PRIMARY KEY AUTOINCREMENT, lastname "
	 "TEXT, firstname TEXT, phonenumber TEXT);"
};
static QString const errorMessage{ "Could now create the table." };
QSqlQuery query;
if (!query.exec(createTableQuery)) {
	 QMessageBox::critical(this, errorMessage, query.lastError().text());
	 std::exit(EXIT_FAILURE);
}
}


Având în vedere că întreaga aplicație se bazează pe acel tabel, nu cred că mai avea sens să o mai las să ruleze. Nu știu dacă abordarea mea este una corectă.

Voi atașa și link-ul de GitHub. Butonul de Delete este dezactivat deocamdată (vezi Issues), altfel restul funcțiilor funcționează. Aș aprecia dacă aș primi și ceva review pe cod.

https://github.com/ISilviu/PhoneBook
De ce nu folosesti try and catch cu delegarea erorilor catre o clasa de tipul exception handler?

#4
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013

Vizualizare mesajdani.user, pe 07 septembrie 2018 - 22:40, a scris:

Dar sa mai separa logica (inclusiv accesul la baza de date) de interfata.
Crezi că urmatorul articol de la Qt ar fi util în acest sens?

Vizualizare mesajCrocodiluMereuVesel, pe 08 septembrie 2018 - 01:29, a scris:

De ce nu folosesti try and catch cu delegarea erorilor catre o clasa de tipul exception handler?
M-am documentat puțin și nu pare cea mai bună idee.

https://stackoverflo...-handling-class

#5
CrocodiluMereuVesel

CrocodiluMereuVesel

    Junior

  • Grup: Members
  • Posts: 361
  • Înscris: 23.07.2018

View Postjegmihai, on 09 septembrie 2018 - 10:15, said:

Crezi că urmatorul articol de la Qt ar fi util în acest sens?


M-am documentat puțin și nu pare cea mai bună idee.

https://stackoverflo...-handling-class
Intersant ce spune mai jos:

Quote

Exception handling is a complicated matter already, and it's difficult enough to get it right without adding extra gears to the machine
e cam naspa cand te iei dupa sfaturile altora care nu inteleg mecanismul try and catch
Vezi tu , atunci cand cineva vede try and catch se asteapta  ca in acea sectiune a codului sa se arunce posibile erori , adica am o interactiune intre codul de bussines-logic(adica un branch)
si codul de actiune al aplicatiei(aka drive-code) adica alt branch.
Cu alte cuvinte intr-un try and catch am o interactiune de branchuri respectiv business-logic branch si drive-code branch din care aplicatia fie poate sa termine sau schimbe business-logic
Practic un programator profesionist senior cand vede un try and catch se asteapta ca sa aiba:
  • intersectie de branchuri de executie al programului

  • posibila  schimbare de executie fie al branchului de business logic fie la drive-codeului.
Ca regula avem asa:
in blocuri IF  THEN ELSE punem doar instructiuni care se refera strict la codul business-logic.
in blocuri TRY AND CATCH punem instructiuni care se refera la interctiunea dintre business-logic si drive-code sau doar drive-code

drive-code =  orice cod scris care nu reprezinta cod de business-logic cum ar fi:
  • pornirea si oprirea aplicatiei,

  • firul principal de executie al programului(schema logica generalista a programului)

  • operatiuni de I/O

  • operatiuni de managment al resurselor(memorie, disc, loguri, etc)

  • operatiuni de negociere asupra protocoalelor (conexiuni cu baze de date , servicii web, etc)

  • operatiuni de interactiune cu alte API-uri
codul de business-logic: codul care implementeaza doar cerintele de business (venite de la client)

Drive-code-ul este suport pt business-logic code

in codul de mai jos :

Quote

if (!query.exec(createTableQuery)) {
		 QMessageBox::critical(this, errorMessage, query.lastError().text());
		 std::exit(EXIT_FAILURE);
}

La prima vedere pare ok, insa din perspectiva une arhitecturi de design software este un pericol: mascheaza executia unui drive-code sub forma  unui eveniment asupra unui business-logic code care
nu exista.
Blocurile if then else atentioneaza ca urmeaza o schimbare de executie doar asupra codului de business-logic
Blocurile  try and catch atentioneaza ca urmeaza o interactiune dintre business-logic si drive-code sau un eveniment important asupra drive-code-ului , eveniment care poate duce la intrerupera aplicatiei

Eu recomand sa  folosesti cu ingredere try and catch si mecanismul fie de tratatea a exceptiilor un  ramura catch fie de delegare a erorilor unui clase de dispatcher de erori (facut de tine personal) .
Acest lucru te va salva pe viitor de multa munca de sapat peste un cod pe care l-ai scris acum 5-6 ani sa zicem.

try {
   query.exec(createTableQuery)
}catch {
  QMessageBox::critical(this, errorMessage, query.lastError().text());
  std::exit(EXIT_FAILURE);
}

deci ai un drive-code

#6
M0sGerila

M0sGerila

    Active Member

  • Grup: Senior Members
  • Posts: 3,892
  • Înscris: 19.12.2007
Care este ideea de a scrie atata cod ?

#7
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013
Am decuplat interacțiunea cu baza de date de codul specific GUI. Păreri?

#8
CrocodiluMereuVesel

CrocodiluMereuVesel

    Junior

  • Grup: Members
  • Posts: 361
  • Înscris: 23.07.2018
Eeee, asa da, alta viata, eh.Deja incepem sa vorbim de design pattern. Adica daca de exemplu ar veni un alt programator care face interfata de mobile, el poate dezvolta propria sa interfata grafica si se contecteaza la codul de interactiune cu baza de date prin intermediul design pattern-ului numit Adapter.

Ei imagineaza-ti la scara larga un proiect unde simultan 3 programatori lucreaza la interfete grafice diferite si altii lucraza la business logic si nici unul din ei nu il influenteaza
pe celalalt deoarece fiecare se conecteaza printr-o interfata, iar schema de legare prin interfete este facuta in prealabil de arhitectul software.

Edited by CrocodiluMereuVesel, 10 September 2018 - 21:55.


#9
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013
M-am documentat despre Model/View și cum este implementat în Qt. Ca View voi folosi QTableView.

Operațiunile de Add și Search sunt relativ ușor de implementat întrucât interacțiunea cu View-ul este minimă.

Pe de altă parte, Update și Delete necesita interacțiune cu View-ul.

În implementarea inițială metodele de Update și Delete se bazau pe faptul că rândul din QListWidget coincide cu id-ul persoanei din baza de date. Problema apărea în momentul în care un contact era șters (id-urile celorlalte rămânând neschimbate, din aceasta cauză am și dezactivat butonul de Delete).

Nu-mi doresc să fac aceeași greșeală din nou, așa că vă intreb: voi cum ați proceda în cazul acestor 2 metode?

#10
M0sGerila

M0sGerila

    Active Member

  • Grup: Senior Members
  • Posts: 3,892
  • Înscris: 19.12.2007
Pai si daca vrei sa stergi o inregistrare din tabel cum faci ?

#11
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013
Aruncă o privire la metoda: void on_deleteButton_clicked().

Voi renunța la ea, motivul l-am expus în postul anterior.

#12
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 24,732
  • Înscris: 10.08.2006
Fa o arhitectura de plug-ins ca lumea, si separa complet business domain de UI.

Pentru UI fa un plug-in.

Pentru baza de date, fa un alt plug-in.

Vei avea un fisier src/plugin/ui/desktop/main.cpp de exemplu, si o clasa plugin::ui::desktop::Plugin.

Plugin insusi ar fi o interfata. Fiecare plugin isi declara dependintele. O clasa Application primeste aceste plugin-uri si le initializeaza in ordinea corecta (topological sorting).

Am descris doar in cuvinte cheie un exemplu de arhitectura curata. Intreaba unde nu intelegi exact ce zic.

In functie de detalii de implementare, poti avea nevoie de AOP, sau nu.

Cert e ca core domain nu va depinde de plug-inuri. Fa cel mai bine un director src/phonebook/ cu toate clasele si interfetele pentru business domain - iti permite sa revizuiesti usor faptul ca respecti principii de baza (directia dependintelor de exemplu).

In main.cpp initializezi Application, adaugi applicatiile rand pe rand, app.addPlugin(new ...); dupa care return app.run().

Ideea e ca intr-un final, aplicatia ta controleaza plugin-urile, nu esti legat de vr-un vendor (Qt sau Sqlite).

Daca vrei sa inlocuiesti unul dintre vendori, adaugi pur si simplu un nou plug-in, si le legi diferit in main.cpp.

Aceleasi idei cu alte cuvinte aici: https://forum.softpe.../#entry23060478

#13
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013

Vizualizare mesajOriginalCopy, pe 11 septembrie 2018 - 22:26, a scris:

si separa complet business domain de UI.
Done.

Pentru început vreau să mă asigur că am facut separarea corect, apoi mă voi muta pe partea de plugins.

#14
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 24,732
  • Înscris: 10.08.2006
Merg mână în mână.

Pentru început crează structura de directoare și mută fisierele.

Ar trebui să ai așa:

src/phonebook/phonebook.cpp
src/plugin/ui/desktop/main.cpp
src/plugin/storage/inmemorysql.cpp

S.a.m.d.

Fiecare director cu treaba lui. Codul însuși nu va fi mult modificat în acest pas. E doar pentru tine să înțelegi modul de gandire, dar ai mult refactoring de făcut în pașii următori.


De exemplu, clasa phonebook din domain nu are ce să caute cu Qt în ea. Qt e un lucru specific pluginului de ui, nu e specific business domain.

#15
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013
OK, am făcut update-ul.

Acum să încerc să pun în aplicare spusele tale din postul #12?

#16
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 24,732
  • Înscris: 10.08.2006
Da. Cel mai bine începi prin a curăța domeniul (src/phonebook). Codul acela trebuie să conțină doar reguli  de business, validare, etc.

Nu are voie să conțină "qt". Codul e util, deci va fi mutat în pluginul de ui.

#17
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,294
  • Înscris: 03.09.2013

Vizualizare mesajOriginalCopy, pe 11 septembrie 2018 - 22:26, a scris:

Plugin insusi ar fi o interfata. Fiecare plugin isi declara dependintele.
Inclusiv detalii din implementarea claselor care vor deriva din interfață?

Să luăm spre exemplu IStoragePlugin (așa îl voi denumi).

Aplicația PhoneBook, așa cum am gândit-o, ar trebui să permită operațiile de CRUD pe o listă de contacte, fiecare contact având nume, prenume și număr de telefon. Prin urmare ea dictează dependințele Plugin-urilor. Problema apare la scrierea propriu-zisă a interfeței IStoragePlugin, nu-mi place că ea te-ar obliga să folosești QString sau să returnezi un QSqlQuery.

class IStoragePlugin
{
public:
virtual ~IStoragePlugin() = default;
virtual auto addContact(QString const& lastName, QString const& firstName, QString const& phoneNumber) -> void = 0;

virtual auto searchContact(QString const& lastName, QString const& firstName) -> QSqlQuery = 0;

virtual auto updateContact(QString const& lastName, QString const& firstName, QString const& phoneNumber, int const id) -> void = 0;

virtual auto deleteContact(int const id) -> void = 0;
}


Templates ar fi soluția în acest caz?

#18
OriginalCopy

OriginalCopy

    I'm harmful, fear me please! :))

  • Grup: Senior Members
  • Posts: 24,732
  • Înscris: 10.08.2006
Metodele acelea sunt specifice unui repository, nu unui plugin.

Plugin e ceva foarte abstract, folosit doar la initializarea și deinitializarea întregii aplicații.

Într-o interfață Plugin ai o metodă de genul getDependencies, și  altele gen init(), run() și shutdown().

Cât despre repository, folosește std::string.

Anunturi


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

Forumul Softpedia foloseste "cookies" pentru a imbunatati experienta utilizatorilor Accept
Pentru detalii si optiuni legate de cookies si datele personale, consultati Politica de utilizare cookies si Politica de confidentialitate