OO-design problemen
Ik ben bezig met het (ter oefening) ontwerpen van een forum in OOP. Nu heb ik twee problemen, en ik wil daar graag jullie oplossing/visie van horen:
1: Een heleboel klassen maken gebruik van PDO. Aangezien ik in PDO geen singleton heb gevonden zoek ik nu naar een andere oplossing om geen overdreven veel verbindingen met de database te maken:
-Ik kan een nieuwe klasse maken die PDO extend, en waar wel een singleton in zit
-Ik kan gebruik maken van persistente connecties, maar ik weet niet tot in hoevere dat handig is
-Ik kan een soort register klasse maken, maar dat heeft ook niet mijn voorkeur...
Heeft iemand hier nog een andere oplossing voor, of wil iemand mij zeggen waarom één van de drie hierboven beschreven oplossingen het beste zijn?
Probleem 2:
Stel ik heb de klasse Topic. Die heeft een compositieverbinding met de klasse Post. Dat houd dus in dat in de klasse topic een array moet zitten waarin alle posts zijn opgeslagen (en uiteraard bijbehorende methoden). In de klasse Post moet een method zitten waar de juiste instantie van Topic in zit (of is dit niet nodig?).
Als ik nu een nieuwe post wil defineren, doe ik dat dan via een methode createPost() (of iets dergelijks) in klasse Topic? Als ik dat namelijk niet doe, en ik maar gewoon rechtstreeks een new Post, dan moet ik hem daarna nog registreren bij een instantie van Topic.
En geldt dan ook dat ik bij het verwijderen van een Post, gewoon een functie deletePost() aanroep? Want anders verwijder ik vrolijk een Post, en dan staat hij nog wel geregistreerd in de instantie van Topic.
Ik hoop dat mijn verhaal een beetje duidelijk is, en alvast bedankt voor het antwoord!
Gewijzigd op 01/01/1970 01:00:00 door Lasse
Persoonlijk zou ik 'm zo-ie-zo in een registery zetten omdat je 'm dan meteen overal tot je beschikking hebt.
Dus het is geen misbruik maar juist goed gebruik als je er wilt voor zorgen dat je per instantie maar 1 connectie wilt ;)
Gewijzigd op 01/01/1970 01:00:00 door Bo az
Maar hoe zit het dan met een persistente verbinding? In principe kan je die ook wel gewoon gebruiken toch?
En weet iemand nog het antwoord op mijn tweede probleem?
apache ab en kijken wanneer hij het opgeeft.
Wat je ook nog kan doen is in plaats van een register gebruiken een Factory voor je klassen (of meerdere factories) gebruiken om de instanties te maken, en de instanties direct te voorzien van een verwijzing naar de instantie van PDO. M.a.w de instantie van PDO door de constructor heen drukken zodat je wel overal maar 1 instantie gebruikt, en vervolgens het 'new Instantie($PDO)' gebeuren laten afhandelen door een aparte klasse/method/functie die die instantie van PDO tot z'n beschikking heeft zodat hij die kan uitdelen aan de nieuw aan te maken instanties.
Dat zou je eens moeten testen. Met de SQL query 'SHOW PROCESSLIST' kan je kijken hoeveel verbindingen er nog open staan, en door te hameren op je server met bijvoorbeeld Wat je ook nog kan doen is in plaats van een register gebruiken een Factory voor je klassen (of meerdere factories) gebruiken om de instanties te maken, en de instanties direct te voorzien van een verwijzing naar de instantie van PDO. M.a.w de instantie van PDO door de constructor heen drukken zodat je wel overal maar 1 instantie gebruikt, en vervolgens het 'new Instantie($PDO)' gebeuren laten afhandelen door een aparte klasse/method/functie die die instantie van PDO tot z'n beschikking heeft zodat hij die kan uitdelen aan de nieuw aan te maken instanties.
En wil er ook nog iemand kijken naar mijn tweede probleem, want ik weet gewoon niet zeker of ik het op die manier goed doe...
Alvast bedankt voor alle reacties!
Ik probeer nu zelf op het moment zo veel mogelijk instanties mee te geven. In het verleden heb ik veel singleton gebruikt, maar dat beviel achteraf niet zo goed (altijd maar 1 instantie, en wanneer je een soort semi-singleton gebruikte, wist je niet meer welke instantie je nu voor je neus kreeg, en waar geef ik de verbindinggegevens mee?) En ook het register-pattern heb ik geprobeerd, maar dan was het vaak maar de vraag of de database al beschikbaar was, en als ik hem vooraf initialiseerde en registreerde, was het maar de vraag of hij wel nodig was. Daarnaast had je relatief weinig controle over welke database hij nu pakte. Ik gebruik bijvoorbeeld meerdere databases, vaak een MySQL of andere standaard-database, en een SQLite voor de cache of de configuratie. Maar dat wisselde nog wel eens (experimenteren e.d.) dus vind ik het tegenwoordig wel handig als ik veel controle over welke database meegegeven wordt, en vind ik het belangrijk dat ik meerdere databases tegelijkertijd kan aanspreken.
Maar die IHGApplication kun je ook tot de controller toch? Maar waarom zou je de database in de controller nodig hebben? Je hebt hem alleen in het Model nodig toch? Dus eigenlijk zeg je: Maak in je controller een instantie van PDO, en geef die bij het instantieren van een klasse steeds door (via de constructor ofzo?).
Hoe initialiseer je meerdere models die uit de database komen als je dat niet via de controller doet? Als je voor ieder model een eigen query gaat uitvoeren ben je nogal inefficiënt bezig.
Stel ik heb een klasse Topic, en daar wil ik alle Posts van hebben. Dan roep ik toch een method van Topic aan, en die gaat alle informatie uit de database halen (zo heb ik het tenminste geleerd). En aangezien Topic onder het type Model valt...
In mijn IHGApplication initialiseer ik een zootje klassen. Helpers, registery, identificatie, configuratie. En dus een database-verbinding (onderdeel van configuratie :) ) Vanuit de IHGApplication instantie wordt dan de URL omgezet in een aanroep naar een Controller-klasse & method, met argumenten. Bij het instantiëren van de controller geef ik de instantie van IHGApplication mee, zodat ik binnen de controller de helpers, configuratie en database e.d. kan gebruiken.
Vanuit de controller wil ik nu bijvoorbeeld 10 personen met een L in de naam opzoeken. Dan maak ik daarvoor een SQL-query, en die prop ik in mijn aangepaste PDO::prepareFor. Als 2e argument geef ik een model waarin de resultaten moeten worden gestopt mee. PDO vraagt nu mijn 10 mensen op, en maakt daar 10 instanties van mijn model voor aan. En geeft bij het aanmaken een verwijzing naar zichzelf mee, zodat ik binnen de models ook een verwijzing naar de goede database heb. Ze komen er immers net uit!
Voor vaker voorkomende queries, bijvoorbeeld 'vind model waarvan primary key = a' gebruik ik wel een simpele aanroep naar het model, maar op zo'n mannier dat de 'factory' eerst wordt aangemaakt door de controller (via een parent-klasse met dit soort handige methods) die dus weer de instantie van de huidige database kan meegeven. $this->models->Persoon->findById(14); Via __get kan je een heleboel gemene trucjes uithalen :) Weer wordt de data via de PDO instantie van IHGApplication (via de controller) gebruikt, en dus weer krijgen alle models een verwijzing naar de database. Dus ik zou in een model Topic nu gewoon findPosts kunnen maken, hij heeft al toegang tot de database.
Let overigens wel op dat je niet alle SQL wilt weglaten of genereren. In mijn idee is dat niet echt handig achteraf. Het staat misschien wel netjes, en in het begin levert het kortere code op, maar wanneer je nu een extra veld wilt selecteren, of een JOIN erbij wilt maken, of een iets ingewikkeldere WHERE-voorwaarde wilt opstellen gaat dat echt enorm onhandig worden. Daarnaast veranderen de queries amper, en is het dus eigenlijk overbodig om ze iedere keer opnieuw te genereren. Wat wel een goeie gewoonte is is om de veelgebruikte queries in methods van een soort factory onder te brengen, zodat je gemakkelijk deze queries kan aanpassen en verbeteren, en je niet gaat kopiëren en plakken. Zodra je dat gaat doen, gaat er waarschijnlijk iets mis. Knippen en plakken is daarentegen weer wel een goeie handeling, want dat betekent dat je constant je indeling zit door te rekenen en te verbeteren ;)
edit: jezus, kan ik serieus geen korte posts meer schrijven tegenwoordig?
Gewijzigd op 01/01/1970 01:00:00 door Jelmer -
Quote:
Gebruik voor dat soort zaken een VIEW, dat maakt de SQL in je PHP-code een heel stuk eenvoudiger.Daarnaast veranderen de queries amper
Daarnaast is een VIEW eenvoudiger te optimaliseren, hier zijn prima indexen op te maken, je weet dat ze niet zo maar veranderen. Dit komt de performance zeker ten goede.
Met VIEW's kun je eveneens de rechten op de database beter gaan regelen, dat zorgt weer voor meer veiligheid, ook altijd een belangrijk onderwerp.
Maar jij werkt waarschijnlijk met één index bestand voor je hele website, waar je dan de juiste controller in included? Persoonlijk houd ik meer van de aanpak dat je verschillende functies van de website ook echt onderbrengt onder meerdere rechtstreeks aan te roepen pagina's. Daarom zal ik dus die IHGApplication niet nodig hebben, want de controller staat gewoon meteen al in de pagina. En het initialiseren van services waar jij het over hebt kan dan gewoon in de desbetreffende controller gebeuren. Maar dat is denk ik maar net welke stijl je preffereert.
Dank jewel voor je uitleg... Daar heb ik heel wat aan.
@pgFrank:
Een view gebruiken is inderdaad ook een goed idee. Bedankt voor de tip!
Stel ik heb de klasse Topic. Deze gaat een compositie aan met de klasse Post. Dat houd dus in dat de klasse Topic een variabele heeft met instanties van Post. Het probleem is nu: Wanneer haal ik welke data op uit de database...
Ik kan bijvoorbeeld meteen als ik een instantie van Topic aanmaak alle Posts ook uit de database halen. Dat met het risico dat al die Post instanties helemaal niet gebruikt gaan worden. Ook kan ik een specifieke post pas uit de database halen als hij ook echt nodig is (bv bij het aanroepen van $topic->nextTopic();). Het nadeel is dan dat ik allemaal aparte query's nodig heb, wat ook niet echt meehelpt voor de performance...
Hoe zouden jullie dit aanpakken? Alvast bedankt voor de hulp.
Code (php)
Nu worden de posts alleen opgehaald wanneer ze voor het eerst nodig zijn. Daarna staan ze in het object zelf en is de database niet meer nodig.
Eventueel kan je nog een aantal en een offset als parameter meegeven aan getPosts, zodat hij daarmee een LIMIT-statement vult. Stel dat je dan een topic hebt met 100 posts en je wil er maar 20 weergeven, dan kan je die andere 80 ongeroerd laten. Het is dan wel handig om ook deze 2 parameters even op te slaan ergens, zodat je kan weten of je meer posts uit de database moet halen of dat de $posts-array vol genoeg is bij een volgende aanroep.