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

#19
Posted 13 September 2018 - 17:19

QSqlQueryModel ar fi un obiect specific plugin-ului de UI?
|
#20
Posted 13 September 2018 - 18:09

OriginalCopy, on 13 septembrie 2018 - 14:38, said:
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(). |
#21
Posted 13 September 2018 - 18:20

jegmihai, on 13 septembrie 2018 - 17:19, said:
QSqlQueryModel ar fi un obiect specific plugin-ului de UI? jegmihai, on 13 septembrie 2018 - 18:09, said:
Am început sa scriu pe partea de plugins. Înainte de a merge mai departe, aș vrea să mă asigur că mă aflu pe calea cea bună. Da, e bine. Metoda init sau cum ai numit-o trebuie totusi sa accepte ca parametru acelasi tip de date ca cel returnat de getDependencies. Nu stiu de ce returnezi vector<Dependency> si nu vector<Plugin> totusi. |
#22
Posted 13 September 2018 - 18:49

src/ui_files/ se muta in desktop plugin, pentru ca apartine acelui plugin.
|
#23
Posted 13 September 2018 - 18:57

OriginalCopy, on 13 septembrie 2018 - 18:20, said:
NumeMetoda init sau cum ai numit-o trebuie totusi sa accepte ca parametru acelasi tip de date ca cel returnat de getDependencies. Altfel nu înțeleg de ce un Plugin ar trebui să poată inițializa pe altul (dacă te referi la metoda init() din IPlugin). |
#24
Posted 13 September 2018 - 19:06

Acum inteleg de ce ai clasa Dependency (am uitat ca suntem in C++), dar ce am zis ramane ca si principiu valabil:
Un plugin nu il initializeaza pe altul. Fiecare plugin isi declara dependintele. Si in metoda init(), fiecare plugin trebuie sa primeasca ca parametru celelalte plugin-uri de care a zis ca depinde. Eu ca plugin, zic ca am nevoie de ceva, si apoi tu ca caller imi dai ce am zis eu ca am nevoie. Cine e "tu ca caller"? Pai Application, o clasa responsabila de initializarea in ordinea corecta a tuturor pluginurilor (chiar si prin interpusi). Aceeasi clasa Application care are metoda execute() sau cum vrei tu s-o numesti, pe care o apelezi in main(). Application nu e nici ea dependenta de niciun vendor. Imagineaza-ti ca eu, plugin de UI, am nevoie sa cer ceva de la read model. Read model e in interiorul lui storage plugin. Deci am nevoie de o instanta a acelui storage plugin, de la care pot cere read model, si face ceva cu el in UI. Si tot asa. |
#25
Posted 13 September 2018 - 22:50

OriginalCopy, on 13 septembrie 2018 - 14:38, said:
Cât despre repository, folosește std::string. Am mai făcut un update pentru partea de plugins. OriginalCopy, on 13 septembrie 2018 - 19:06, said:
Si in metoda init(), fiecare plugin trebuie sa primeasca ca parametru celelalte plugin-uri de care a zis ca depinde. În implementarea actuală simt că am cheat-uit acest lucru. |
#26
Posted 13 September 2018 - 23:03

jegmihai, on 13 septembrie 2018 - 22:50, said:
Aceasta fiind o modalitate de asigurare că celelalte plugins există și nimic mai mult? În implementarea actuală simt că am cheat-uit acest lucru. Daca eu, plugin de UI, am nevoie de read model, si nu reusesc sa pun mana pe un read model, atunci nu pot functiona cum trebuie. |
#27
Posted 14 September 2018 - 13:49

E vital. Nu poate functiona altfel.
Daca eu, plugin de UI, am nevoie de read model, si nu reusesc sa pun mana pe un read model, atunci nu pot functiona cum trebuie.
Imagineaza-ti ca eu, plugin de UI, am nevoie sa cer ceva de la read model. Read model e in interiorul lui storage plugin. Deci am nevoie de o instanta a acelui storage plugin, de la care pot cere read model, si face ceva cu el in UI. |
#28
Posted 14 September 2018 - 14:54

În acest caz, problema ar fi următoarea: singurul loc în care cele 2 plugins ar putea interacționa este clasa PhoneBook, însă clasa PhoneBook nu știe nimic altceva despre ele decât că sunt niște IPlugin-uri cu metodele aferente interfeței. |
|
#29
Posted 14 September 2018 - 15:22

Fa un sistem mai abstract. Asa nu e abstract, e cod duplicat: https://github.com/I...k/phonebook.cpp
Lucreaza doar cu IPlugin in PhoneBook (presupun ca asta e clasa pe care eu am numit-o mai sus "Application"). Domeniul de business (tot codul din src/phonebook/) nu are voie sa depinda de un plugin sau altul. Asta ar viola definitia de plugin. Domeniul are voie sa stie doar de notiunea de "plugin", deci sa aiba o singura metoda: addPlugin(). Domeniul nu stie si nu ii pasa ce face fiecare plugin. Pluginurile pot depinde intre ele, si pot depinde de business model, niciodata invers (=definitia lui "plugin"). In momentul in care o clasa e mentionata intr-o compilation unit, spunem ca acea compilation unit depinde de acea clasa. "Plugin" e doar ceea ce respecta directia clara de dependinte, asa cum am descris-o mai sus. Altfel nu se numeste plugin, ci tightly coupled component. Edited by OriginalCopy, 14 September 2018 - 15:50. |
#30
Posted 14 September 2018 - 16:47

Ar fi firesc ca un anumit plugin să poată fi inițializat fără vreo dependință?
InMemorySQLiteStoragePlugin nu pare să depindă de nimeni. |
#31
Posted 14 September 2018 - 17:47

Ar fi firesc ca un anumit plugin să poată fi inițializat fără vreo dependință? InMemorySQLiteStoragePlugin nu pare să depindă de nimeni. Bineînțeles. Doar food for thought: ar putea depinde de un plugin de logging. Sau unul de caching. Acestea sunt "cross-cutting concerns" întâlnite în mod comun în aplicații, cum le-am menționat aici: https://forum.softpe.../#entry23060478 Și nu e musai să le faci ca pluginuri, există și metoda prin AOP, sau multe alte variații, în funcție de alte detalii. Menționez doar așa ca fapt divers, ca să conectez diferite postări ale mele, poate le citești sau le-ai citit, și vrei să le integrezi undeva în sistemul tău propriu de gândire. Unele pluginuri chiar e strict necesar să nu aibă nicio dependență. Vrei într-un final să ai un graf aciclic. Dacă ai unul ciclic, atunci e extrem, extrem de probabil să fi făcut o greșeală în arhitectură. |
#32
Posted 14 September 2018 - 18:00

Am făcut un update pe GitHub. Aplicația funcționează, însă nu sunt sigur dacă totul este așa cum ar trebui.
Am și o nelămurire. Aici ai spus așa:
Pai Application, o clasa responsabila de initializarea in ordinea corecta a tuturor pluginurilor (chiar si prin interpusi). Apoi ai spus așa:
Domeniul are voie sa stie doar de notiunea de "plugin", deci sa aiba o singura metoda: addPlugin(). Domeniul nu stie si nu ii pasa ce face fiecare plugin. |
#33
Posted 14 September 2018 - 18:18

"initializarea" adica apelarea metodelor init() in ordinea corecta ***. Nu "instantierea". Instantierea fiecarui plugin o vei face cel mai probabil in main.cpp.
*** pentru a indeplini aceasta misiune, trebuie sa apelezi getDependencies() mai intai pentru fiecare plugin, si o clasa Graph ("interpusul" mentionat in alta postare) cu care faci sortarea topologica. Acest intreg sistem de pluginuri mai elaborat e poate prea complicat pentru aplicatia de fata. Poti avea doar init(), pe care il apelezi direct in main pentru fiecare plugin, in ordinea corecta. Depinde de ce urmaresti. Daca vrei doar sa GTD, dar totusi o separare curata, atunci sterge getDependencies() si asambleaza-ti aplicatia direct in main, manual, fara Graph si toate cele. Daca vrei sa iti faci o infrastructura de cod reutilizabila si in aplicatii mai complexe, poti face de la inceput aceasta intreaga poveste cu getDependencies(). Sau faci totul in main() deocamdata, si dupa ce aplicatia e "gata", introduci si sistemul de plugins mai sistematic cu getDependencies() (e destul de simplu, o data ce ai oricum codul separat frumos). Greseala pe care o vad momentan e ca getDependencies() returneaza vector<Plugin*>: un plugin nu poate returna pointeri la alte pluginuri. vector<Dependency> era corect. init() insa primeste un vector de pluginuri (cu pluginurile specificate de getDependencies). Nu stiu exact unde in modul de gandire a fost greseala ta, dar asta e manifestarea greselii de gandire in cod pe care o vad. Deci, ai doua optiuni mari si late: 1. instantierea si initializarea in main(), totul manual 2. instantierea in main(), addPlugin() pentru fiecare, si return application.run() in main(). pluginul UI va fi executat ultimul datorita sortarii topologice, si va bloca main thread. initializarea se face de catre Application (=PhoneBook) cu ajutorul interpusului Graph. Edited by OriginalCopy, 14 September 2018 - 18:23. |
|
#34
Posted 15 September 2018 - 19:03

Pentru început voi merge pe approach-ul numărul 1. Îmi rezerv puțin timp pentru a mă documenta cu privire la topological sorting.
Referitor la următoarea remarcă: Quote
Lines 28-29 should be just one: return uiPlugin.run(); Astfel aș fi nevoit să modific interfața și să oblig orice plugin să returneze o valoare în urma metodei run(). Pentru UiPlugin acest lucru este total în regulă, însă, spre exemplu, storage plugin nu are nevoie să returneze o valoare. Dacă s-ar întampla ceva ce nu trebuie, el ar arunca o excepție. |
#35
Posted 17 September 2018 - 12:46

#36
Posted 17 September 2018 - 14:10

Dacă s-ar întampla ceva ce nu trebuie, el ar arunca o excepție. Complexitatea unui plugin e mare, iar in componentele mari, complexe, vei avea cel mai probabil si clase de exceptii dedicate. De exemplu NoDisplayException - aruncat de UI daca aplicatia e rulata pe un server (care nu are display). Vrei ca aceasta exceptie sa ajunga in apelant? Asadar, apelantul (core domain) trebuie sa stie de modul de comunicare cu userul? Sau cu alte cuvinte: sa cuplezi domeniul de un plugin? Pai asta ar viola principiul mentionat anterior: definitia unui plugin = depinde de alte pluginuri si de core domain; definitia core domain = nu depinde de nimic. Ai viola directia dependintelor. Pe langa asta, o eroare e cel mai bine tratata exact in locul in care ea are loc. De ce? Pentru ca in acel loc ai contextul cel mai larg, ai cele mai multe informatii despre ce s-a intamplat si de ce, ai toate variabilele ***. Motivul pentru care uneori nu tratezi local eroarea e ca ai clase de erori care pot fi tratate similar. Acele erori au loc in diferite parti ale codului. *** daca nu ai acces la toate variabilele, direct sau indirect, e pentru ca ai un "neg" in arhitectura, nu ai injectat consistent toate dependintele de care ai nevoie. Una dintre principiile usoare in arhitectura e sa ai straturi (layered architecture). Aplicatia privita din avion e cu un core domain, in care inserezi niste pluginuri. Din avion, arhitectura fiecarui plugin nu conteaza, e un detaliu de implementare, insa in interiorul pluginului ai iar alte straturi. Un alt motiv pentru care nu tratezi local eroarea e deoarece poti generaliza eroarea si prin generalizare, o poti trata (catch block) intr-un layer de mai de sus in arhitectura. Acum, cat de sus permiti exceptiilor sa "bubble up" pana cand le prinzi si tratezi (try catch)? Dupa parerea mea, ultimul transon de aparare impotriva unei exceptii e in plugin (chiar in metoda run). Asa, poti izola mult mai punctual o eroare. De ce vad asa lucrurile? Una dintre motivatiile de a separa in pluginuri e ca poti imparti responsabilitati intr-o echipa: un programator e responsabil de pluginul X, altul de pluginul Y, s.a.m.d. (cu overlap si alte chestii, irelevante in aceasta discutie). In momentul in care in log vezi ca o eroare a aparut in pluginul de UI, ii pui in brate ticketul din bug tracker persoanei cu cea mai multa experienta in acea zona fara foarte mult efort: nu trebuie sa stai sa inspectezi eroarea, sa incerci sa o reproduci timp de 1-2 zile, doar ca sa iti dai seama cine trebuie sa o rezolve. Astfel cresti eficienta echipei. Vrei sa cresti numarul de evenimente "persoana potrivita la problema potrivita". |
Anunturi
▶ 0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users