[oop] constructor als "uitvoerder"?
Pagina: « vorige 1 2 3 volgende »
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.
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:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
<?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);
?>
// 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:
... 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?
... 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.
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.
Ben jij het dan niet eens met wat Ward hier zegt?
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.
Code (php)
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.
In jou geval is execute() een mooie methode die je public kan maken en die de taken van de constructor op zich neemt.
>> 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?
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.
Nee, ik heb de meningen nu wel zo'n beetje op een rijtje, alhoewel het nog steeds aan mij is om de discussie door te zetten ;)
>> En "de klasse doet niks zonder die method call" is iets anders als "de klasse kan niks zonder die method call".
Daar heb je een goed punt.
Dan wordt het toch een public execute method. Bedankt allemaal voor het meedenken en de verschillende visies!
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.
Puur gezien vanuit readability van de code vind ik dat beter dan
Maar uiteindelijk is het natuurlijk je eigen keuze :)
Verder ben ik het er ook mee eens dat de constructor alleen maar gebruikt moet worden voor het initialiseren van het object, en eventueel valideren van de binnenkomende variablen. Stel je voor dat je Processor::execute methode een fout geeft bij het versturen van een response ($this->sendResponse()), en het enige dat ik, als externe developer die met jou code werkt, heb gedaan is een nieuw Processor object aanmaken zou ik wel even raar opkijken.
@Ward:
>> Als je altijd in twee stappen dit moet doen voordat je een bruikbare $foo hebt, dan deugt het ontwerp van de klasse niet:
Hier heb ik nog een vraagje over.
Stel dat je de Processor class hebt en het enige wat je in de constructor doet is het request setten, en verder niks. In principe is de class zelf nu dus gebruiksklaar.
Nu is de volgende stap in het proces dat je een aantal dingen wilt initialiseren, je wilt bijv. de services laden en je wilt wat gegevens inladen (bijv. paden). In feite is dit dus ook een vorm van initialiseren. Hoort dit dan ook thuis in de constructor?
@NOLot:
>> ... en je weet 100% zeker dat er geen extra methodes of dependencies bij komen kun je er ook een static methode van maken. e.g.
Waarom dan een statische methode en niet dit:
Wat is precies het verschil?
Bij een statische methode afhankelijk ben je afhankelijk van de variablen die meegegeven worden. Als je er een class van maakt kun je nog extra instellingen meegeven via andere methodes (zoals bijvoorbeeld het meegeven van een logger aan het object). Maar dit staat toch helemaal los van je vraag over het uitvoeren van logica in de constructor?
Zoals je zelf al zegt: je doet het in dit geval al in de constructor, omdat je Processor anders ongeldig is.
Dat is het doorslaggevende criterium: de constructor levert een geldig object op. De enkele aanroep van de klasse met new ... moet een valid object opleveren. Daar moet niet nog eens init() of iets dergelijks voor initialiseren of een andere vorm van het "booten" of "opstarten" van het object overheen.
>> Nu is de volgende stap in het proces dat je een aantal dingen wilt initialiseren, je wilt bijv. de services laden en je wilt wat gegevens inladen (bijv. paden). In feite is dit dus ook een vorm van initialiseren. Hoort dit dan ook thuis in de constructor?
Dat klinkt als een verzameling van verschillende taken en verschillende verantwoordelijkheden — en dus verschillende klassen. Maar dit is strikt genomen meer configureren dan initialiseren.
Even een voorbeeldje met een databaseverbinding. Die open je liever zó:
dan zo:
Code (php)
1
2
3
4
5
6
7
2
3
4
5
6
7
<?php
$dbh = new Database();
$dbh->setServerByName($servername);
$dbh->setUserName($username);
$dbh->setPassword($password);
$dbh->connect();
?>
$dbh = new Database();
$dbh->setServerByName($servername);
$dbh->setUserName($username);
$dbh->setPassword($password);
$dbh->connect();
?>
Het kán wel op de tweede manier — en incidenteel heb je het misschien zelfs per se nodig — maar fraai is anders.
Ik snap het nog steeds niet (sorry). Mijn vraag was of de constructor logica mag uitvoeren. Het antwoord daarop is nee. Oké prima. Maar dan moet er dus wel een execute method worden aangeroepen van buitenaf. Toen zei jij dat je dat dan met een statische methode zou doen:
Op zich vind ik dat wel mooi om op deze manier te doen, maar mijn vraag is waarom je voor een statische methode kiest in plaats van dit:
Misschien moet ik de vraag iets anders stellen. Wanneer kies je voor een statische aanroep?
@Ward:
>> Dat klinkt als een verzameling van verschillende taken en verschillende verantwoordelijkheden — en dus verschillende klassen. Maar dit is strikt genomen meer configureren dan initialiseren.
Klopt, het wordt ook door verschillende classes uitgevoerd, maar het moet vanuit de processor worden getriggerd. Deze handelingen moeten gebeuren voordat de execute method wordt aangeroepen. Dus dan kan ik het vanuit de constructor doen... maar dan doet de constructor dus eigenlijk te veel, of ik ga de functie handmatig aanroepen, maar dan krijg je dus weer een extra init() aanroep en dat wilden we ook niet. Het enige dat dan overblijft, is dat de execute method die handelingen gaat uitvoeren/triggeren. Ik denk dat dat dan de beste oplossing is.
Ozzie PHP op 19/05/2014 13:45:10:
Ik keek naar je gegeven voorbeeld, je hebt een class die 1 variable krijgt ($request) en maar 1 ding doet (execute). Geen extra dependencies of whatsoever. Maar goed hierover verschillen de meningen. Er is geen één manier binnen het programmeren, en ik zou gaan wat voor jezelf het duidelijkst is
Ik vind de oplossing die jij aandraagt wel een mooie. In één regel is duidelijk wat er gebeurt. Ik denk dus dat ik het op deze manier ga doen.
Maar zo'n oplossing gebruik je dus als een class maar 1 ding kan doen (1 method heef) en verder geen dependencies heeft. Correct?
Zo'n oplossing gebruik ik wanneer ik er zin in heb :D Maar vooral voor dat soort situatie's inderdaad
Lol... :)
Ozzie PHP op 19/05/2014 14:09:59:
Maar zo'n oplossing gebruik je dus als een class maar 1 ding kan doen (1 method heef) en verder geen dependencies heeft. Correct?
Die dependencies zijn nu juist een deel van het probleem: je hebt een kernelprocessor met afhankelijkheden die dóór die afhankelijkheden eerst moet worden geconfigureerd voordat deze kan worden geïnitialiseerd :-)
Dependencies zijn ook een deel van de oplossing, want je kunt de drie verantwoordelijkheden instellen/controleren/uitvoeren ook splitsen, bijvoorbeeld in: