Een Processor/Core/Kernel class doorloopt allerlei stappen. Mag de consructor deze stappen triggeren/uitvoeren? Even een heeeeel erg versimpeld voorbeeldje met 3 fictieve functies.
<?php
class Processor {
private $request;
public function __constructor($request) {
$this->request = $request.
$this->initializeRequest();
$this->executeRequest();
$this->sendResponse();
}
}
?>
Zou de constructor deze functies mogen triggeren? Is dat oké volgens het OOP principe?
>> Dat je dan net zo goed die execute method kunt aanroepen vanuit de constructor?
Nee, want de constructor is voor constructie. Als je een huis bouwt dan weet je dat er mensen in gaan wonen. Dat hoeft nog niet te betekenen dat de arbeiders meteen erin moeten gaan wonen... Toch gaat het bij elke bouw hetzelfde: Constructor maakt huis; makelaar zoekt bewoner; bewoner woont in huis.
Ik vind het nu wel wat lastig. Ik pak even de eerdere uitspraken van Ward erbij.
Het gaat in deze specifieke situatie om het toevoegen van paden aan een object. Niet helemaal hetzelfde, maar het gaat even om wat Ward hierover zegt:
Ward van der Put op 24/10/2013 08:58:11
Kun je iets nuttigs doen na new Paths() zonder de methode Paths::add() te gebruiken? Is het antwoord "Nee", dan hoort add() bij het initialisatieproces en zou ik een constructor gebruiken die drie prototypen ondersteunt en de keuze overlaat aan de gebruikers van de klasse:
<?php
// Duidelijk gestructureerd en overzichtelijk.
$paths = new Paths();
$paths->add($paths_data1);
$paths->add($paths_data2);
// Dit is heel logisch ...
$paths = new Paths($paths_data1);
$paths->add($paths_data2);
// ... als dit ook wordt ondersteund.
$paths = new Paths($paths_data);
?>
Vervolgens reageer ik daar als volgt op:
Ozzie PHP op 24/10/2013 09:07:46
Het adden van de paden is in mijn ogen geen wezenlijk onderdeel van het "gebruiksklaar" maken van de class. Want als ik gewoon dit doe:
<?php
$paths = new Paths();
?>
... dan werkt de class prima, en kan ik gewoon gebruik maken van de add() method. En daarom denk ik dus dat het toevoegen van paden niet thuishoort in de constructor. Ben je het daar mee eens, of niet?
En Ward weer:
Ward van der Put op 24/10/2013 09:17:37
Ja en nee. De uiterste consequentie van deze argumentatie is namelijk dat je helemaal nooit een constructor nodig hebt: je kunt immers altijd alles naar methoden delegeren.
Als je na new Paths() slechts een lege huls hebt en je meestal pas na add() iets zinvols kunt doen, dan zou ik de constructor inzetten.
Een constructor maakt het object klaar voor gebruik. Zoals ward zegt, als je na de constructor een object hebt waarmee je niks kan voordat je een bepaalde method hebt uitgevoerd doe je wat verkeerd.
Dat is hier echter helemaal niet het geval. Je kan perfect het object gebruiken zonder de request af te handelen.
Kun je dat toelichten Wouter? Het enige wat die class doet is het request afhandelen. Hij doet niks anders.... stel dat ik de execute method public maak, dan is het ook de enige method die ik kan aanroepen. Ik snap het verschil dus niet helemaal.
public function __constructor($foo, $bar) {
$this->foo= $foo.
$this->initializeFoo();
$this->executeFoo();
$this->doTheFoo();
$this->addSomeBar($bar);
$this->createFooBar();
}
}
?>
Dit voorbeeld genomen Ozzie;
Het werkt, De een zegt dat het mag, de ander van niet.
Echter staat er één ding vast en dat is dat dit een ultiem voorbeeld is van inflexibiliteit. Ik wijs naar mijn eerdere opmerking dat je later niet meer om de code in de constructor heen kunt. en dat kan dus slecht uitpakken later. Dan wil je die constructor alsnog gaan verbouwen en loopt je eerder geschreven code in de soep.
Ozzie, je moet proberen zo min mogelijk de constructor te gebruiken. Het enige waar de constructor voor gebruikt dient te worden is om bij aanmaak van het object dingen toe te wijzen. De constructor mag naar mijn mening geen commando bevatten om dingen uit te voeren dat moet je delegeren naar een methode. Jij wilt soms heel ingewikkeld denken maar soms is het gewoon heel simpel.
In jou geval is execute() een mooie methode die je public kan maken en die de taken van de constructor op zich neemt.
>> De constructor mag naar mijn mening geen commando bevatten om dingen uit te voeren dat moet je delegeren naar een methode.
>> In jou geval is execute() een mooie methode die je public kan maken en die de taken van de constructor op zich neemt.
Oké... maar dan heeft die hele class maar 1 method die kan worden aangeroepen. En die ene method MOET ook worden aangeroepen, anders doet de class niks. Is dat dan niet raar?
Anders gezegds, als er toch maar 1 method is die je kunt aanroepen en die MOET worden uitgevoerd, zou dat dan geen reden kunnen zijn om de method vanuit de constructor aan te roepen?
>> zou dat dan geen reden kunnen zijn om de method vanuit de constructor aan te roepen?
Nee, want een constructor is voor constructie van een object. Een method is voor de uitvoering van een object.
En "de klasse doet niks zonder die method call" is iets anders als "de klasse kan niks zonder die method call".
Ik weet niet hoe vaak ik en andere mensen dit nog in dit topic moeten plaatsen. Maak nu je keuze: "Ja, ik luister naar Dos, Frank, Reshad, Wouter, Ward" of "Nee, ik doe mijn eigen willetje". Ik respecteer beide keuzes, maar het heeft niet heel veel zin deze "ik wil het"/"nee, doe nou niet" discussie verder voort te zetten.
Ozzie, mijn toelichting ging over het initialiseren van een object. Dát moet je vermijden, want dat is de taak van de constructor.
Als je altijd in twee stappen dit moet doen voordat je een bruikbare $foo hebt, dan deugt het ontwerp van de klasse niet:
$foo = new Foo();
$foo->init();
Niet altijd de betrouwbaarste bron, maar zoals Wikipedia scbrijft over constructors: “They have the task of initializing the object’s data members and of establishing the invariant of the class, failing if the invariant is invalid. A properly written constructor leaves the resulting object in a valid state.”
Als je ná $foo = new Foo(); een ongeldig object hebt en het object pas later met $foo->init(); geldig wordt, dan deugt het ontwerp dus niet: het object is tijdelijk invalid. Het mist dan bovendien een klasse-invariant (of heeft een invariant die onwaar is en van buiten de klasse waar moet worden gemaakt). Hier had je de constructor moeten gebruiken.