class zonder public method?
Ik heb een autoload functie in een class staan. Nu heb ik in die class ook een register method staan die die de autoload functie, jawel... registreert :)
In mijn code hoef ik dan alleen dit te doen:
Een tijdje terug hadden we het in een ander topic over het constructen van classes en wanneer je dat doet. Ward gaf toen aan dat als je een class hebt waarbij je altijd dit doet:
Dat je dan net zo goed doFoo() vanuit de constructor kunt laten uitvoeren.
Bij mijn autoloader class heb je nu zo'n zelfde situatie. De enige method die je kunt aanroepen is register. Dus het enige wat je met die class kunt doen is dit:
Nu vraag ik me dus af of ik dan niet beter de register method vanuit de constructor kan triggeren. Als ik dan de autoloader wil registreren, dan hoef ik alleen nog maar dit te doen:
Op zich wel lekker kort, maar is dit gebruikelijk? In principe zie je nu in de code niet wat er gebeurt, maar dat zou je met commentaar kunnen ondervangen:
Graag jullie reactie.
Hmm goede vraag, over het algemeen wil je alles zo succint mogelijk hebben, dat betekent dat methodes en constructors datgene doen wat ze te horen doen. Als je deze regels na leeft, betekent dat je zelf de register() methode zult moeten aanroepen.
Suc-wat?
De vraag is dus inderdaad of je zelf die methode moet willen aanroepen. Je zou ook kunnen zeggen dat de class niet werkt zonder dat die method is aangeroepen, en dat je 'm daarom dus vanuit de constructor aanroept. Daarnaast wil je ook niet dat de register method meer dan 1x wordt aangeroepen. Dus ook dat zou een goede reden kunnen zijn om de constructor te gebruiken. Maar goed, ik weet dus niet of zoiets gebruikelijk is.
Laat ik er nog eens een ander voorbeeld bij geven:
Stel we hebben een class die iets configureert. Dat configureren moet eenmalig gebeuren.
Je zou dan dit kunnen doen:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$fc = new fooConfigurator($foo_data);
$fc->configure();
$new_foo = $fc->get();
?>
$fc = new fooConfigurator($foo_data);
$fc->configure();
$new_foo = $fc->get();
?>
Echter, je zou ook het configureren vanuit de constructor kunnen triggeren:
In het 1e voorbeeld zie je in de code wat er gebeurt. Echter, het enige wat ik van die class wil, is de geconfigureerde gegevens terugkrijgen. Dus het enige wat je eigenlijk aan die class wil vragen, is "geef mij de geconfigureerde gegevens". En als je het zo bekijkt, waarom zou je dan een public configure() method willen hebben?
Hoe dan ook, volgens mij is het gewoon weer zo'n 'wat heb je liever' vraag. Beide kan, verschil is minimaal en beide zijn duidelijk. Kies dus gewoon zelf en verdoe er niet veel meer tijd aan. Een 'absoluut' antwoord is er toch niet op te geven.
Allright, thanks Erwin. Ik dacht dat het misschien een rare constructie is om een soort "zelfregulerende" class te maken waar je geen variabele aan toekent en waarvan je geen enkele method aanroept. Maar als dat gewoon oké is, dan doet we dat gewoon! :)
Even breder trekken, als het enige wat jou autoloader doet is register() en je hebt een kale constructor, waarom zou je dat in een aparte methode zetten?
Waarom zou je wat in een aparte methode zetten? Je bedoelt de register functie?
Kun je na $foo = new Foo() helemaal niets zonder $foo->doFoo()? Dan hoort de aanroep van Foo::doFoo() volgens mij inderdaad in de constructor. Het draait erom dat de constructor een bruikbaar object oplevert; lukt dat nooit zonder een of meer methoden, dan roep je die in de constructor aan. Je mag hier dus ook een fout verwachten: "Ik kan zonder succesvolle Foo::doFoo() onmogelijk een object van de klasse Foo maken."
Overigens kunnen die vereiste methoden dan zowel public als private zijn. Die keuze staat daar eigenlijk los van.
Het draait er, meer in het algemeen, om dat je niet "een PHP-object" krijgt, maar "een geldig Foo-object". Na bijvoorbeeld $db = new mysqli() verwacht je ook niet zomaar "een PHP-object", maar een MySQLi-object.
Zomaar "een PHP-object" maken kan namelijk ook zo:
Code (php)
Gewijzigd op 11/11/2013 13:19:27 door Ward van der Put
>> kun je in de constructor met $this->register() een private function register() aanroepen.
Precies hoe ik het in gedachten had!
Merk op dat de autoloader wel meer methods moet hebben dan alleen register. Je moet kunnen aangeven waar een namespace gevonden kan worden in de directory structure
Dat kan in sommige gevallen nodig zijn, maar in dit geval is het niet nodig.
Ozzie PHP op 11/11/2013 13:32:06:
>> Je moet kunnen aangeven waar een namespace gevonden kan worden in de directory structure
Dat kan in sommige gevallen nodig zijn, maar in dit geval is het niet nodig.
Dat kan in sommige gevallen nodig zijn, maar in dit geval is het niet nodig.
Wat is dan je reden om een object aan te maken?
Als je alles gaat hardcoden zou je ook een functie/static method/lambda kunnen registreren als autoloader. Daar zal maar een instantie van zijn. Waarom je meerdere instanties van een Autoloader object waar je niets aan kan instellen zou willen kunnen hebben snap ik niet.
Alles in een class stoppen puur omdat het kan is in elk geval geen goede reden.
Als ik alleen 'new Autoloader();' in een code base zie staan is het mij niet meteen duidelijk of dit correct is. Ik weet niet wat er in de constructor gebeurd zonder het op te zoeken, wat mijn IDE makkelijk maakt ALS de constructor/classe gedocumenteerd is met phpdoc blocks.
Het gebruik van een methode als 'Autoloader::register( [$autoloader = NULL] )' zou een stuk duidelijker zijn.
Ik ben het trouwens niet eens dat register() in dit geval deel moet zijn van de constructor. Als voorbeeld:
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
// https://gist.github.com/jwage/221634
include 'SplClassLoader.php';
$classLoader = new SPLClassLoader();
// doet het prima, het hoeft niet geregistreerd te staan als autoloader om te werken
$classLoader->loadClass('Meow');
// laten we $classLoader als nog maar eens registreren als autoloader
$classLoader->register();
?>
// https://gist.github.com/jwage/221634
include 'SplClassLoader.php';
$classLoader = new SPLClassLoader();
// doet het prima, het hoeft niet geregistreerd te staan als autoloader om te werken
$classLoader->loadClass('Meow');
// laten we $classLoader als nog maar eens registreren als autoloader
$classLoader->register();
?>
De class hoort het niet uit te maken of het als autoloader geregistreerd staat of niet. De class moet doen wat er van de class gevraagd wordt, of ik het nou vraag, of PHP's core maakt niet uit.
Gewijzigd op 11/11/2013 14:12:52 door Dos Moonen
Merk overigens op dat ik het over een Autoloader heb en niet over een class loader. Ik heb geen public loadClass() method nodig.
Gewijzigd op 11/11/2013 14:17:13 door Ozzie PHP
Ozzie PHP op 11/11/2013 14:13:32:
het is de autoloader voor m'n eigen framework. Die hoeft in dit geval niet flexibel te zijn.
En wat zijn je redenen om een Autoloader object te creëren in plaats van een functie/static method aan te roepen?
Mijn mening is dat als zoiets als dit nooit hoort tegen te komen:
Het is te magisch.
Gewijzigd op 11/11/2013 14:33:10 door Dos Moonen
Wat bedoel je met "te magisch"? Je vindt het te onduidelijk wat er gebeurt ofzo?
Ozzie PHP op 11/11/2013 14:13:32:
Dan nog heeft Dos inderdaad een punt, meerdere zelfs.@Dos: het is de autoloader voor m'n eigen framework. Die hoeft in dit geval niet flexibel te zijn. Maar je hebt gelijk dat er situaties zijn waarin dat zeker wel wenselijk is.
Merk overigens op dat ik het over een Autoloader heb en niet over een class loader. Ik heb geen public loadClass() method nodig.
Merk overigens op dat ik het over een Autoloader heb en niet over een class loader. Ik heb geen public loadClass() method nodig.
Voor de performance zou ik van een autoloader die één keer wordt geladen en daarna alle class loading voor één namespace (je framework) regelt in jouw geval geen klasse maken. Dan zou ik het in drie stappen anders doen:
• registreer rechtstreeks één autoload-functie (in plaats van een klasse);
• laat die autoload-functie relatieve in absolute paden omzetten (meetbaar sneller);
• prepend de autoload-functie (zodat deze als eerste aan de beurt is en niet — je zal ze ertussen hebben zitten — een autoloader die een directoryscan gebruikt).
Maar waar laat je die dan? Zet je die functie in je bootstrap?
>> laat die autoload-functie relatieve in absolute paden omzetten (meetbaar sneller);
Hij gebruikt nu al absolute paden.
>> prepend de autoload-functie (zodat deze als eerste aan de beurt is en niet — je zal ze ertussen hebben zitten — een autoloader die een directoryscan gebruikt).
Wat bedoel je precies met prepend? Ik heb maar 1 autoloader.
Maar waarom niet in een class? Dat zal qua performance toch niks uitmaken?
Ozzie PHP op 11/11/2013 14:36:40:
Wat bedoel je met "te magisch"? Je vindt het te onduidelijk wat er gebeurt ofzo?
Ja. Je creëert een Autoloader instantie en je doet er vervolgens niets mee. Maar op magische wijzigen gaat een ander deel van je codebase daardoor blijkbaar werken.
Mijn eerste gedachte zal zijn "Oh, die instantie zal nooit gebruikt worden. Dan hoef ik dat object ook nooit aan te maken."
Het mag voor jou dan duidelijk zijn, maar het is geen goede gewoonte om aan te leren. Begin er dus a.u.b. ook niet aan.
Gewijzigd op 11/11/2013 14:51:27 door Dos Moonen
Oké, maar dat was dus ook precies mijn vraag.
Eerder had ik met Ward een discussie of je een method die altijd moet worden aangeroepen wel of niet in de constructor moet stoppen. De conclusie was toen, zie ook hierboven, dat als iets altijd moet gebeuren, dat je dat door de constructor moet laten doen.
Stel je hebt een class die gegevens moet configureren, even weer het voorbeeld van hierboven, dan kun je dit doen:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$fc = new fooConfigurator($foo_data);
$fc->configure();
$new_foo = $fc->get();
?>
$fc = new fooConfigurator($foo_data);
$fc->configure();
$new_foo = $fc->get();
?>
of dit:
In dit laatste voorbeeld zal de constructor intern de configure() method aanroepen. Op zich is dat niet heel vreemd. Het enige wat jij als gebruiker kunt doen, is de data terug ophalen. Dus... je stopt iets in de configurator new fooConfigurator($foo_data) en vervologens haal je het er weer uit $new_foo = $fc->get(). Het zou dan toch eigenlijk ook onzinnig zijn om handmatig de configure() method nog eens aan te roepen?