Jump to content

SUBIECTE NOI
« 1 / 5 »
RSS
Incalzire casa fara gaz/lemne

Incalzire in pardoseala etapizata

Suprataxa card energie?!

Cum era nivelul de trai cam din a...
 probleme cu ochelarii

Impozite pe proprietati de anul v...

teava rezistenta panou apa calda

Acces in Curte din Drum National
 Sub mobila de bucatarie si sub fr...

Rezultat RMN

Numar circuite IPAT si prindere t...

Pareri brgimportchina.ro - teapa ...
 Lucruri inaintea vremurilor lor

Discuții despre TVR Sport HD.

Cost abonament clinica privata

Tremura toata, dar nu de la ro...
 

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

* * * - - 2 votes
  • Please log in to reply
183 replies to this topic

#37
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
Am înțeles. Se poate spune și că excepțiile pe care le prindeam in main.cpp făceau un leak în privința modului în care funcționează storagePlugin? La urma urmei, el putea să fie orice altceva decât o bază de date embedded (spre exemplu un STL container).

Concret, responsabilitatea lui core domain este de a se asigura că plugin-ul respectiv rulează, fără să-l intereseze motivele?

#38
jegmihai

jegmihai

    Senior Member

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

View PostOriginalCopy, on 17 septembrie 2018 - 14:10, said:

nu ai injectat consistent toate dependintele de care ai nevoie.
Termenul mă duce cu gândul la Dependency Injection.

#39
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 17 septembrie 2018 - 19:08, said:

Am înțeles. Se poate spune și că excepțiile pe care le prindeam in main.cpp făceau un leak în privința modului în care funcționează storagePlugin? La urma urmei, el putea să fie orice altceva decât o bază de date embedded (spre exemplu un STL container).
Nu "se poate spune", ci fix asta spunem. "leaky abstractions".

View Postjegmihai, on 17 septembrie 2018 - 19:08, said:

Concret, responsabilitatea lui core domain este de a se asigura că plugin-ul respectiv rulează, fără să-l intereseze motivele?
Da si nu.

Pana acum in discutiile noastre am simplificat putin lucrurile pentru directorul src/phonebook/ si la un moment viitor urmeaza sa faci refactoring conform cu ce voi scrie mai jos:

Momentan in src/phonebook/ ai doua componente cu o relatie simbiotica intre ele. La nivel conceptual ele sunt diferite, insa momentan ai acolo un amestec greu de distins la nivel de cod.

1. cod de infrastructura
2. domain model

Codul de infrastructura sunt clase precum Application. Acele clase care pot fi reutilizate intr-o arhitectura similara pentru o alta aplicatie

Domain model este reprezentarea programatica, formala, a regulilor de business. Bine, in aplicatia de fata ai un domain model anemic (chiar titlul proiectului zice ca e o simpla aplicatie crud), dar cred ca intelegi unde bat: exersezi pentru proiecte mai complicate ca acest "crud app - phone book".

In domain model scopul tau e sa scrii cod atat de expresiv, incat chiar si product owner (clientul care plateste pentru program) poate intelege ce se intampla, ce face codul, fara cunostinte de programare. Nu e SF, tipic pentru acesti oameni e gandirea analitica, deci inteleg codul. Personal touch: discutiile cu PO pe cod sunt discutiile mele favorite. Pe langa faptul ca sunt amuzante si creative, ii si da lui PO increderea ca stii ceea ce faci, caci si el insusi se simte in controlul proiectului. Asta iti aduce si tie beneficii profesionale.

Alt avantaj al unui domain model expresiv e ca reguli de business noi devin emergente. Exemplu: PO vrea un nou feature, si e mult mai evident si mai clar la nivel de cod concret unde si cum e cel mai bine sa integrezi acel cod in cadrul src/phonebook/ (in cazul nostru). De ce? Deoarece codul nu mai e incarcat cu un numar mare de linii de cod ce tin de fie baza de date, fie ui, fie logging, caching, cu ceva algoritm relevant pentru business rules presarat pe ici pe colo, printre infinitul de linii irelevante.

Tot din acelasi rationament: cand creezi un nou feature, observi mult mai usor patterns si le poti extrage in abstractii (clase, componente) comune, reutilizabile. Uneori in astfel de situatii se intampla chiar sa te faca sa vii cu idei de solutii noi la probleme, deoarece codul insusi iti sopteste ce se intampla de fapt. Cand esti in mijlocul discutiei PO si spui abordari  la care PO nu s-a gandit, iti creste valoarea.

Deci, da, responsabilitatea lui 1. cod de infrastructura este sa se asigure ca pluginurile ruleaza, sau daca esueaza, ca aplicatia esueaza in mod corect (true positive si true negative).

Responsabilitatea lui 2 e sa modeleze business rules, algoritmii, etc.

View Postjegmihai, on 17 septembrie 2018 - 19:27, said:

Termenul mă duce cu gândul la Dependency Injection.
Da, la asta m-am referit. Atentie totusi la buzzwords: nu inseamna ca trebuie sa folosesti cine stie ce framework fancy sau mai stiu eu ce aberatii. DI e o chestiune foarte simpla, usoara de facut intr-un "greenfield project": componente mai abstracte (mai sus in piramida de layers - think layered architecture) primesc ca dependinte componente la acelasi nivel de abstractizare, si apoi decompun acea dependinta si trimit catre layerele mai dedesubt alte dependinte mai granulare.

Gandeste-te concret la proiectul nostru: plugins primesc ca parametru alte plugins ca dependinte, si isi extrag din ele alte componente mai granulare (ex: ReadModel mentionat intr-o postare anterioara), si le paseaza in adancime altor componente din sie insasi (=acelasi plugin). Concret: nu stiu ce dialog primeste ca parametru doar ce are el nevoie: obiect ReadModel).

DI*** in action e acea metoda Plugin::init, sau MyDialog::MyDialog(ReadModel...). Nimic complicat, fancy sau mai stiu eu ce, ci solutii simple la probleme simple.

*** DI poate insemna dependency injection sau dependency inversion. Spunem ca: MyDialog respecta dependency inversion, iar apelantul sau injecteaza dependintele (dependency injection). Cele doua semnificatii ale lui "DI" sunt deci simbiotice.

Edited by OriginalCopy, 17 September 2018 - 19:57.


#40
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
OK, am revenit cu un update referitor la tratarea excepțiilor, acestea nu mai interacționează cu codul specific aplicației.

#41
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 17 septembrie 2018 - 20:55, said:

OK, am revenit cu un update referitor la tratarea excepțiilor, acestea nu mai interacționează cu codul specific aplicației.
Super! Daca privesti critic codul cum e el, ce alt refactoring ai face in pasul urmator? Separarea infrastructure vs. business model vine mai incolo ;) Lucruri simple, destul de minore.

#42
jegmihai

jegmihai

    Senior Member

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

View PostOriginalCopy, on 17 septembrie 2018 - 19:55, said:

Deci, da, responsabilitatea lui 1. cod de infrastructura este sa se asigure ca pluginurile ruleaza, sau daca esueaza, ca aplicatia esueaza in mod corect (true positive si true negative).

Responsabilitatea lui 2 e sa modeleze business rules, algoritmii, etc.
Să luam ca exemplu un sistem de rezervări pentru un cinematograf.

Business rules s-ar putea referi la:
  • faptul că o persoană trebuie să-și ofere datele personale în schimbul serviciului?
  • faptul că o persoană nu poate rezerva mai mult de 5 locuri?
  • faptul că sistemul nu ar mai trebui să permită rezervări dacă toate locurile s-au ocupat?

View PostOriginalCopy, on 17 septembrie 2018 - 20:59, said:

Daca privesti critic codul cum e el, ce alt refactoring ai face in pasul urmator?
Din moment ce ai menționat că separarea infrastructure/business model se va face mai încolo, deduc că te referi la refactoring care să nu schimbe arhitectura aplicației.

Cod specific C++?

#43
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006
Da, aia e esenta lui "domain model". Nu inseamna ca ai strict doar liniile de cod gen "if(reservation.getSeats() > 5) return false", poti avea multe clase care fac lucruri diferite, poti folosi actor model ca arhitectura interna a lui domain model, sau "event-based model", s.a.m.d. Dar codul din src/phonebook/ nu e legat de niciun vendor, e doar cod C++ "curat", indiferent de abordari.

Da, fara schimbari arhitecturale deocamdata, doar "clean-ups". Curatenia la care ma astept nu e specifica C++.

Eu am cateva lucruri in minte, dar nu ma astept sa zici exact ce vreau eu. Vreau doar sa exersezi "identificarea de pasi de urmat" si sa ii prioritizezi in mod rezonabil, daca ai mai multi pasi.

#44
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
Nu-mi place bucata asta:

void UiMainWidget::init(std::vector<IPlugin*> const& dependencies)
{
if (typeid(dependencies.front()) != typeid(_dependencies.front()))
	 throw NonMatchingDependencyTypeException(
		 "The provided plugin is not the needed one.");
ui.setupUi(this);
_storagePlugin = dynamic_cast<InMemorySQLiteStoragePlugin*>(dependencies.front());
ui.tableView->setModel(_storagePlugin->getModel());
}


Am trișat prin faptul că știu că Ui depinde doar de un plugin. Trebuia să vin cu o soluție mai generalistă.

#45
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 17 septembrie 2018 - 21:58, said:

Nu-mi place bucata asta:

void UiMainWidget::init(std::vector<IPlugin*> const& dependencies)
{
if (typeid(dependencies.front()) != typeid(_dependencies.front()))
	 throw NonMatchingDependencyTypeException(
		 "The provided plugin is not the needed one.");
ui.setupUi(this);
_storagePlugin = dynamic_cast<InMemorySQLiteStoragePlugin*>(dependencies.front());
ui.tableView->setModel(_storagePlugin->getModel());
}


Am trișat prin faptul că știu că Ui depinde doar de un plugin. Trebuia să vin cu o soluție mai generalistă.
De acord, e urat, dar nu sunt sigur ca motivatia ta e corecta.

Problema e ca nu faci ce am zis intr-o postare anterioara: nu destructurezi dependintele in UiPlugin. Bine ar fi sa ai _widget.init(this->_readModel); in UiPlugin si in UiPlugin::init sa iti extragi _readModel din DB plugin. Sau altfel spus, dynamic_cast mutat cu un layer mai sus, din widgetul concret in plugin.

De ce? Pai mai multi widgets vor avea nevoie de read model, nu e o chestiune limitata la acel widget.

Altceva? Enumera cativa pasi, si ordinea lor.

Edited by OriginalCopy, 17 September 2018 - 22:12.


#46
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
1. Mut vector<IPlugin*> _dependencies, respectiv InMemorySQLiteStoragePlugin _storagePlugin din widget în UiPlugin.
2. Reimplementez metodele init() in clasele respective.

#47
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 17 septembrie 2018 - 22:36, said:

1. Mut vector<IPlugin*> _dependencies, respectiv InMemorySQLiteStoragePlugin _storagePlugin din widget în UiPlugin.
2. Reimplementez metodele init() in clasele respective.
Ok.

Dar chiar ai nevoie de storage plugin salvata in ui plugin? Nu e suficient sa extragi ce ai nevoie din storage plugin in init(), si sa salvezi doar acele servicii oferite de storage in ui?

Cu storage plugin in ui plugin nu faci decat sa creezi tentatii, dar nu rezolvi o problema concreta.

#48
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
Done.

Am avut o singură problemă, pe care am rezolvat-o mutând QSqlQueryModel din StoragePlugin în InMemorySQLiteDatabase. Încă mă gândesc dacă se putea mai bine.

#49
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006
Momentan layerele de sub plugin (detaliile/arhitectura fiecarui plugin) au intortocheri de parca ar fi pomul de craciun.

Dar inainte sa ne uitam la ele, vezi aici: https://github.com/I...tree/master/src ceva de reparat?

#50
jegmihai

jegmihai

    Senior Member

  • Grup: Senior Members
  • Posts: 11,536
  • Înscris: 03.09.2013
  • Dialog-urile mutate în plugin/ui/desktop.
  • Șterse CouldNotOpenDatabaseException.h/.cpp.


#51
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 18 septembrie 2018 - 11:46, said:

Încă mă gândesc dacă se putea mai bine.
Ok, implementeaza cand ai o solutie, sau vino cu posibile abordari si avantajele/dezavantajele fiecareia, daca ai mai multe si nu stii ce sa alegi.

Nu stiu daca si in ce masura urmatoarele principii te vor ajuta sau daca sunt aplicabile chiar acum in situatia ta, dar sunt demne de tinut minte in general:
  • un plugin e o componenta; daca vrei ca acea componenta sa fie inlocuibila, trebuie ca in exterior sa expui interfete, nu clase concrete, dar poate mai important de atat: in exterior nu ai voie sa expui detalii de implementare, si nici atat vendori (leaky abstractions); vezi graficul de pe pagina: https://en.wikipedia...mponent_diagram - ex: in coltul din stanga sus ai "IPolicyService"
  • vrei sa ai toate obiectele intr-o stare valida; nu vrei sa introduci metode publice sau protected (public + protected => public API) care fac doar jumatate din munca, asa incat ai nevoie sa apelezi diferite metode pentru a iti indeplini misiunea, intr-o anumita ordine; o operatie (un apel de public API) trebuie sa iti lase obiectul intr-un state valid
  • exemplificare mai clara a punctului anterior: evita setterii; fiecare setter in plus creste "suprafata de contact" a unei componente, si in mod exponential combinatiile in care poti introduce buguri intr-un sistem; mai toate lucrurile pot fi injectate prin constructor intr-o arhitectura curata; situatiile in care un setter e necesar sunt atat de restranse, incat poti avea chiar si 0 sau 1 setter in proiecte de zeci de mii de linii de cod - de exemplu in cazul unor dependinte circulare


#52
jegmihai

jegmihai

    Senior Member

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

View PostOriginalCopy, on 18 septembrie 2018 - 13:03, said:

Ok, implementeaza cand ai o solutie, sau vino cu posibile abordari si avantajele/dezavantajele fiecareia, daca ai mai multe si nu stii ce sa alegi.
Prima abordare ar fi implementarea curentă.

A doua abordare ar fi să mut QSqlQueryModel în SQLiteDatabase (clasa părinte), iar InMemorySQLiteDatabase să-l moștenească. Nici acest approach nu mi se pare cel mai bun.

Mă mai gândesc.

View PostOriginalCopy, on 18 septembrie 2018 - 13:03, said:

in exterior nu ai voie sa expui detalii de implementare
Aici expun un detaliu de implementare?  

class InMemorySQLiteStoragePlugin : public IPlugin
{
public:
	// std::vector<IPlugin*> getDependencies() const;
	void init() override;
	int run() override;
	void shutDown() override;
	InMemorySQLiteDatabase _database;
private:
	void init(std::vector<IPlugin*> const& dependencies) override{};
};


Plugin-ul pune la dispoziție metode (prin intermediul lui InMemorySQLiteDatabase) care i-ar putea corupe state-ul (așa cum ai menționat la punctul 2). Mda, cred că întrebarea mea devine retorică.

View PostOriginalCopy, on 18 septembrie 2018 - 13:03, said:

evita setterii
Îmi amintesc că am mai citit același sfat venind tot din partea ta. Și da, încerc să evit setterii pe cât posibil.

Edited by jegmihai, 18 September 2018 - 17:50.


#53
OriginalCopy

OriginalCopy

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

  • Grup: Senior Members
  • Posts: 27,268
  • Înscris: 10.08.2006

View Postjegmihai, on 18 septembrie 2018 - 17:48, said:

Aici expun un detaliu de implementare?  
Da. Operatiile pe baza de date se fac cel mai bine prin repositories.

View Postjegmihai, on 18 septembrie 2018 - 17:48, said:

Mă mai gândesc.
Stii/nu ai uitat de "prefer composition over inheritance", nu?

#54
dani.user

dani.user

    Guru Member

  • Grup: Senior Members
  • Posts: 30,194
  • Înscris: 24.02.2007
Cateva observatii, pe partea specifica C++:

  • std::runtime_error ofera deja what(). N-are sens sa-l suprascrii fara sa aduci functionalitate suplimentara.
  • Noile standarde suporta override pentru a indica clar ca suprascrii o metoda.
  • Durata de viata a instantelor e controlata, in mod traditional, cu ajutorul constructorilor si a destructorilor. E cam ciudat sa vezi metode gen shutdown.
  • Pointeri drept membrii ai unei clase sunt de evitat pe cat posibil. Nu indica nimic despre "al cui mai e si asta?". Asta e mai greu de evitat in cazul Qt datorita modului de gestiune al memoriei ales de acest framework.
  • Specificarea return type-ului la final, ca aici auto createModel() noexcept->void;, mi se pare oribila. Are sens doar cand ai decltype si trebuie neaparat sa pui la final.
  • Observ multa logica dublata doar pentru a putea pasa cand std::string, cand QString. De cand cu std::string_view nu prea mai exista motive de a transmite std::string drept parametru.

Edited by dani.user, 18 September 2018 - 20:15.


Anunturi

Chirurgia endoscopică a hipofizei Chirurgia endoscopică a hipofizei

"Standardul de aur" în chirurgia hipofizară îl reprezintă endoscopia transnazală transsfenoidală.

Echipa NeuroHope este antrenată în unul din cele mai mari centre de chirurgie a hipofizei din Europa, Spitalul Foch din Paris, centrul în care a fost introdus pentru prima dată endoscopul în chirurgia transnazală a hipofizei, de către neurochirurgul francez Guiot. Pe lângă tumorile cu origine hipofizară, prin tehnicile endoscopice transnazale pot fi abordate numeroase alte patologii neurochirurgicale.

www.neurohope.ro

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