dubbelop of niet?
Stel ik heb deze class:
Code (php)
Nu vraag ik me het volgende af.
In de __construct method geef ik aan dat $foo een object moet zijn van het type Foo. Vervolgens doe ik in de private method setFoo precies hetzelfde. Ook hier geef ik aan dat een object van het type Foo verwacht wordt. Nu twijfel ik tussen deze 2 beredeneringen:
a) dit is dubbelop, het is voldoende om alleen in de __construct method aan te geven dat een object van het type Foo wordt verwacht.
b) zo is het juist duidelijk, omdat je zowel in de __construct method als setFoo method precies kunt zien welk type object er wordt verwacht.
Graag jullie mening. Wat is juist, beredenering a of b?
B. (kort antwoord, ik weet t)
Thanks!
Volgens mij zou de setter moeten bepalen wat er geset mag worden en daar zou het dus in elk geval moeten staan.
Dat gezegd hebbende, ik ga mee met Frank. Het is niet dubbelop, voor beide methods geldt dat het een Foo object moet zijn. Dat de ene de andere aanroept is daarbij van ondergeschikt belang in mijn ogen.
Het zou onlogisch zijn om het wel bij je setter maar niet bij je constructor aan te geven. Dan lijkt het alsof je constructor meer accepteert terwijl dat niet het geval is. En je private methode gebruik je op meer plekken (anders is die misschien overbodig) waardoor het handig/netjes/wenselijk is om het daarbij ook aan te geven.
Ja, het is theoretisch gezien dubbelop om het zowel bij de constructor als bij de setter in te stellen.
Ja, het is totaal verkeerd om het wel bij de constructor en niet bij de setter te doen.
Ja, het is niet overzichtelijk voor de constructor om het alleen bij de setter te doen.
Mijn oplossing: PHPdocs!!!
Ward, ja, maar daar gaat het nu totaal niet over...
Wouter J op 26/02/2013 11:32:16:
Wel als je Bar op meerdere plaatsen expliciet maakt, want dan heb je alleen in de kop use Ozzie\Framework\Barbamamma as Bar; nodig wanneer je later liever Barbamamma dan Barbapappa gebruikt als Bar.Ward, ja, maar daar gaat het nu totaal niet over...
De vraag was immers of wat dubbelop is. Een nieuwe Bar met zoeken en vervangen op meerdere plaatsen moeten implementeren voorkom je met use. Dat is namelijk pas echt dubbelop en bovendien gevoelig voor fouten.
@Wouter J, fatsoenlijke phpdoc is inderdaad een must. Maar nu mis je zelf de typehinting in de constructor. Dat was toch niet je bedoeling?
Strict genomen is het genoeg om de typehinting in de setter alleen te plaatsen. De setter bepaalt namelijk wat er wel en niet geset mag worden, niet de constructor. Mocht je dan iets anders meegeven dan zal de setter alsnog de foutmelding geven.
Ik vind de C optie... alleen in de setter aangeven wel type object het moet zijn enigszins vreemd. Ik snap de beredenering dat de setter bepaalt wat het moet zijn, maar de waarde die vanuit de constructor wordt doorgegeven mag niks anders zijn dan een object van het type Foo. Dan is het toch raar dat je daar alles toestaat?
Ik gebruik nu een aparte setFoo functie. Maar ik had ook vanuit de constructor gelijk dit kunnen doen:
$this->foo = $foo;
Dan had je toch ook verplicht een obejct van het type Foo nodig gehad?
Je weet dat de $foo vanuit de constructor per se een Foo object moet zijn, dus waarom het daar dan ook niet meteen verplicht stellen?
Quote:
Je weet dat de $foo vanuit de constructor per se een Foo object moet zijn, dus waarom het daar dan ook niet meteen verplicht stellen?
Omdat het niet de taak is van de constructor om te beslissen wat $this->foo bevat en wat er mee gedaan wordt als het een verkeerde waarde bevat.
Jij bent nu aan het scripten volgens mijn setter beleid, maar bent nog aan het denken volgens het constructor beleid. Dat werkt natuurlijk niet :)
Laat ik eens een praktijk voorbeeld geven...
Stel we hebben een fabriek die auto's in elkaar zet van het merk BMW. Om die auto's in elkaar te zetten wordt de fabriek bevoorraad door een extern bedrijf. Dit externe bedrijf levert ook nog onderdelen aan andere fabrieken van andere merken.
Op een mooie zonnige dag rijdt de vrachtwagen van het externe bedrijf naar de BMW fabriek, gevuld met onderdelen... voor Opel auto's! Oeps... foutje! De vrachtwagen met Opel onderdelen komt bij de poort van de BMW fabriek (let op, dit is de __construct functie). De lading wordt niet gecontroleerd en de vrachtwagen mag doorrijden. Het fabrieksterrein is groot en de vrachtwagen moet een paar kilometer verder rijden. Na 15 minuten bereikt hij de fabriekshal, parkeert zijn vrachtwagen tegen het laad-dock. Een ploeg van 10 medewerkers gaat de vrachtwagen uitladen. Alle onderdelen worden op pallets gestapeld en stuk voor stuk naar de machine gebracht waar ze moeten worden verwerkt. We zijn inmiddels anderhalf uur verder en 20 mensen hebben zich nu in totaal al met de vracht beziggehouden. Dan zet een machinewerker de machine aan en pakt een van de onderdelen erbij. "Hé!" roept hij, "dit zijn helemaal geen BMW onderdelen!" Het productieproces wordt onmiddelijk stopgezet en de BMW fabriek heeft een hoop schade opgelopen.
Was het dan niet handiger geweest als we direct aan de poort al even hadden gecontroleerd of in de vrachtwagen van het externe bedrijf wel de juiste (BMW) onderdelen zaten?
In je code wacht je niet anderhalf uur lang (nog niet eens anderhalve miliseconde) tot je de setter aanroept. Zodra je het object dus aanmaakt krijg je direct een foutmelding om je oren. Omdat het in de constructor fout gaat (ook al loopt het dan nog via een setter) wordt het object niet eens aangemaakt (je krijgt een fatal error). Wat is het verschil met het typehinten in de constructor?
De class is verantwoordelijk voor zijn eigen doen en laten en moet er voor zorgen dat het ofwel goed kan werken, ofwel om hulp roept via een exception. Dat gebeurt in dit geval. Als je een verkeerde variabele meegeeft zal het object niet worden aangemaakt.
We hebben een OpenComponents class. Die geven we mee aan BmwFactory#construct. Deze geeft deze waarde direct door aan BmwFactory#setComponents. Die komt er direct achter dat de waarde verkeerd is en geeft dat direct aan. Nog voor er ook maar iets 'uitgeladen' is of voordat er '15 minuten rij tijd' aan vooraf is gegaan. Al met al maakt het misschien 1/1000 nanoseconde uit, dat gaat Bmw echt niet merken in hun afzet.
Waar het mij om gaat is dit...
Als ik iets meegeef aan de constructor zie ik gelijk dat er een bepaald object wordt verwacht. Ik hoef dan niet eerst in die class te gaan kijken. Jullie geven het dan aan in de PHPDoc, maar dat vind ik zelf niet handig. Mijn vraag is dan ook, wat is er "fout" om het aan de poort (__construct) gelijk even te controleren? Dit zorgt mijns insziens alleen maar voor extra duidelijkheid juist.
Als je zo'n ik-ben-beter-dan-mijn-developer-editor hebt (met autocompletion enzo) is PHPdoc vaak ook gesupport, waardoor het even duidelijk wordt als dat je typehints geeft. Want om typehints te zien zonder zo'n ik-ben-beter-dan-mijn-developer-editor moet je ook in de broncode kijken.
Als je het echt duidelijk wilt maken dan sla je die hele constructor over en ga je methods gebruiken als setComponentsWhichAcceptsOnlyABmwComponentsClassWhichImplementsTheComponentsInterfaceAndHasAMethodWhichIsCalledGetComponentsSoThatWeCanUseThoseComponentsToCreateYourCar. Dan weet je zeker dat je nooit meer fouten gaat maken...
Gewijzigd op 26/02/2013 15:37:46 door Wouter J
Dussss... :-/
Php handelt altijd alles af in 1 ruk.
Alles wat met een object gebeurt, gebeurt nanoseconden nadat de constructor is uitgevoerd.
Vergelijk even met een windows applicatie; een GUI met formulier.
Daar kan je dus een object aanmaken in het begin.
user = new User();
user->init(); // voer hier dingen uit die sowieso moeten gebeuren
En naar mate de gebruiker iets invult, dikt de informatie van het object aan. Alles is dan event driven
user->setName( imputField.value )
In php heb je de luxe om alles te herschikken. Je hebt sowieso alle gegevens; dus of het in de constructor gebeurt; via setters; met een init(); ... het is voornamelijk een kwestie van smaak.
Gewijzigd op 26/02/2013 16:16:57 door Kris Peeters
Ozzie PHP op 26/02/2013 15:30:53:
Als ik iets meegeef aan de constructor zie ik gelijk dat er een bepaald object wordt verwacht. Ik hoef dan niet eerst in die class te gaan kijken. Jullie geven het dan aan in de PHPDoc, maar dat vind ik zelf niet handig. Mijn vraag is dan ook, wat is er "fout" om het aan de poort (__construct) gelijk even te controleren? Dit zorgt mijns insziens alleen maar voor extra duidelijkheid juist.
Van theorie weer terug naar praktijk.
optie a) je hebt setters en getters die alle verantwoordelijkheid nemen voor het setten en getten van de properties, ook intern.
optie b) een class moet autonoom kunnen werken, maar intern hoeft niet alles met setters en getters te gebeuren.
Zoals al eerder gemerkt (in een ander topic) is Wouter van categorie a en in dat geval bepaalt de setter dus welke type object er geset moet worden. De constructor moet daarvan afblijven. Als je namelijk morgen bepaalt dat er ook een ander type mag worden gebruikt dan moet je naast de setter ook de constructor aanpassen en dat wil je nu net niet.
Ik ben meer van optie b. Wat mij betreft hoeft het binnen een class niet allemaal 100% op die manier geregeld te worden. Ik zou dus wel ook in de constructor de type hint meegeven. Waarschijnlijk zou ik dan in de constructor ook direct het property setten en niet via de setter.
a beter dan b? Of andersom? Ligt eraan wat je keuze is. Voor beide valt wat te zeggen en je hebt voor beide argumenten voor en tegen.
Wat jij dus moet doen.... is een biertje pakken en achterover hangend de finale keuze maken in welk kamp je gaat zitten, voor de komende tijd dan ;-)