![]() |
Second Opinion
Folosind serviciul second opinion ne puteți trimite RMN-uri, CT -uri, angiografii, fișiere .pdf, documente medicale. Astfel vă vom putea da o opinie neurochirurgicală, fără ca aceasta să poată înlocui un consult de specialitate. Răspunsurile vor fi date prin e-mail în cel mai scurt timp posibil (de obicei în mai putin de 24 de ore, dar nu mai mult de 48 de ore). Second opinion – Neurohope este un serviciu gratuit. www.neurohope.ro |
Baze de date embedded – exercitiu practic (pentru programatori mid-level)
Last Updated: Oct 10 2018 14:21, Started by
jegmihai
, Sep 07 2018 22:27
·
0

#1
Posted 07 September 2018 - 22:27

OK, sper să reușesc să o fac cât mai curând. Am decis să încerc următorul challenge propus de @dani.user:
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
Posted 07 September 2018 - 22:40

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
Posted 08 September 2018 - 01:29

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 |
#4
Posted 09 September 2018 - 10:15

Dar sa mai separa logica (inclusiv accesul la baza de date) de interfata.
De ce nu folosesti try and catch cu delegarea erorilor catre o clasa de tipul exception handler? https://stackoverflo...-handling-class |
#5
Posted 09 September 2018 - 10:59

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 Quote
Exception handling is a complicated matter already, and it's difficult enough to get it right without adding extra gears to the machine 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:
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:
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); } 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 |
#7
Posted 10 September 2018 - 20:38

Am decuplat interacțiunea cu baza de date de codul specific GUI. Păreri?
|
#8
Posted 10 September 2018 - 21:52

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
Posted 11 September 2018 - 20:29

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
Posted 11 September 2018 - 20:55

Pai si daca vrei sa stergi o inregistrare din tabel cum faci ?
|
|
#11
Posted 11 September 2018 - 21:30

Aruncă o privire la metoda: void on_deleteButton_clicked().
Voi renunța la ea, motivul l-am expus în postul anterior. |
#12
Posted 11 September 2018 - 22:26

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
Posted 12 September 2018 - 18:03

#14
Posted 12 September 2018 - 19:16

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
Posted 12 September 2018 - 20:08

OK, am făcut update-ul.
Acum să încerc să pun în aplicare spusele tale din postul #12? |
|
#16
Posted 13 September 2018 - 06:17

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
Posted 13 September 2018 - 13:57

Plugin insusi ar fi o interfata. Fiecare plugin isi declara dependintele. 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
Posted 13 September 2018 - 14:38

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