Ola peepz,

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:

<?php
$autoloader = new Autoloader();
$autoloader->register();
?>
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:

<?php
$foo = new Foo();
$foo->doFoo();
?>
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:

<?php
$autoloader = new Autoloader();
$autoloader->register();
?>
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:

<?php
new Autoloader();
?>
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:

<?php
// Register the autoload method.
new Autoloader();
?>
Graag jullie reactie.
> De een zegt dit, de ander dat.
En dus doe jij wat jij het mooist vind.
jawel... maar het is niet leuk als het de ene week "blauw" is en de andere week ineens "geel"
Ozzie PHP op 11/11/2013 20:52:33

Oké... maar iemand anders op het forum zegt dat wanneer je een object aanmaakt het altijd zo goed als gebruiksklaar moet zijn.

Dit hoeft niet perse in strijd te zijn met mijn verwachtingen van constructors. De vraag tijdens het schrijven van de constructor is dan "Wat is verplicht, wat is optioneel, en welke optionele dingen wil ik via de constructor in kunnen stellen?"
Op basis van welke parameters ik aan de constructor geef krijg in een object in de staat (state) die ik wil.
Of dit volledig geïnitialiseert is, of maar voor een deel hangt er vanaf wat ik nodig heb.
Of de constructor zo geschreven is dat het object altijd volledig geïnitialiseert uit de constructor komt, of niet hangt er vanaf wat ik nodig heb.


Zoiets is een stuk beter dan een class met alleen maar non-public methods waar jij de instatie nooit van gebruikt.
<?php

class Autoloader
{

public static function init(...) {

...

spl_autoload_register(array('Autoloader', 'load'));
}

public static function deinit() {
...

spl_autoload_unregister(array('Autoloader', 'load'));
}

public static function load($class) {
...
}
}

?>
Dankjewel voor je voorbeeld DOS. Wat is precies de meerwaarde om alle methods statisch te maken?

(Wanneer zou je trouwens een autoloader willen unregisteren?)
De meerwaarde?
<?php

new Autoloader;
// versus
Autoloader::init();

?>
Zoals ik eerder zei, als ik 'new Autoloader;' zie staan -zonder het op te slaan in een variabele- ga ik er vanuit dat het weg kan. Constructors initialiseren objecten. Maar met het object wordt niets gedaan. Ik zal er waarschijnlijk niet van uit gaan dat de environment geïnitialiseert wordt door de constructor van Autoloader. De scope van een constructor is het object.

Je zou ook de hele Autoloader::init() in de global scope in je bootstrap/kernel/whatever kunnen zetten. Ik heb geen sterke voorkeur zolang je het maar niet vanuit een constructor doet!


Je kan autoloaders unregisteren om onnodige aanroepen naar die autoloader te voorkomen wanneer je voorbij een punt bent waar je die autoloader nodig had om library X correct te laten werken.
Stel je gebruikt een library om emails te versturen. De emails zijn verstuurd. Vervolgens ga je een library gebruiken om een form op te bouwen. Elk element wordt voorgesteld door zijn eigen class. Dat betekend veel calls naar de autoloaders. Waarom zou je de email library's autoloader nog laten zoeken? Wat is het nut? Het kost alleen maar tijd.
>> Constructors initialiseren objecten. Maar met het object wordt niets gedaan

Oké... en nu wordt het dus even lastig te begrijpen voor mij. Ik heb hier namelijk onlangs een hele discussie over gehad hier op het forum. Ik ging er vanuit dat een constructor uitsluitend bedoeld was om een class "gebruiksklaar" te maken.

Ik ga je een voorbeeld geven en dan ben ik benieuwd hoe jij dat ziet.

Stel ik heb een class waarin ik paths kan opslaan. Deze paths worden voorafgegaan door een path_prefix. Mijn idee was dan ongeveer dit:

<?php
$path_prefix = 'root/foo/bar/';
$paths = new Paths($path_prefix);
$paths->add($paths_array);
?>
Wat je dus ziet is dat ik de $path_prefix meegeef aan de constructor. De constructor set $path_prefix als class property. Dit was mijn idee van wat een constructor behoort te doen. De class "gebruiksklaar" maken. Nu is de class gebruiksklaar en kun je er paden aan toevoegen.

Vorige week werd me verteld dat dat niet goed is, omdat je op deze manier direct na het constructen van de class een leeg "omhulsel" hebt. Je hebt een class waar niks in zit. Dat zou niet moeten. Daarom moet je de $paths_array direct al via de constructor meegeven:

<?php
$paths = new Paths($paths_array, $path_prefix);
?>
Er werd dus gezegd dat het "vullen" van een class door de constructor moet gebeuren, zodat je meteen na het instantiëren een "gevuld" object hebt en niet een leeg omhulsel.

Maar jij lijkt nu weer iets anders te zeggen, dus ik begin het spoor een beetje kwijt te raken... Snap je?

>> Je kan autoloaders unregisteren om ...

Ah, oke... op die manier. Duidelijk!
Programmeurs zullen vaak vuist regels voorschotelen alsof het een natuurwet is. Over het algemeen komt er dan vanzelf wel een punt waar je er achterkomt dat het niet altijd zo hoeft. En soms haalt iemand als ik opeens je hele wereld over de kop.

<?php
$path_prefix = 'root/foo/bar/';
$paths = new Paths($path_prefix);

... // misschien een paar regels verder, misschien 8 calls dieper, wie weet
$paths->add($paths_array);
?>
Dit is MISSCHIEN gewild. Het kan zijn dat het nodig is om een kip-of-het-ei probleem te voorkomen. Of een andere zeldzame reden.

<?php
$paths = new Paths($paths_array, $path_prefix);
?>
Dat zal over het algemeen gewild zijn. Volgens de vuistregel is dat hoe je het doet. Het feit dat ik zeg dat het niet altijd het geval is moet je voorlopig proberen te vergeten.
Maar als dit is hoe het in het algemeen is:

<?php
$paths = new Paths($paths_array, $path_prefix);
?>
Dan wordt er toch ook "onderwater" een method uitgevoerd die je niet ziet?

En dan kom ik weer bij mijn eerdere voorbeeld, waar ik eigenlijk telkens geen antwoord op krijg...

Stel we hebben een taart die door een class in stukken moet worden gesneden... wie moet dan de opdracht geven om de taart in stukken te snijden:

Hier geef ik de opdracht zelf:
<?php
$taart_snijder = new TaartSnijder($taart);
$taart_snijder->snijTaart();
$gesneden_taart = $taart_snijder->get();
?>
Maar ik kan ook de constructor de opdracht laten geven, en dan ziet de code er zo uit:

<?php
$taart_snijder = new TaartSnijder($taart);
$gesneden_taart = $taart_snijder->get();
?>
Beiden stukjes code doen hetzelfde, maar wat is de juiste manier? De taart moet altijd in stukken worden gesneden, dus zou ik denken dat de constructor hier opdracht voor mag geven... ???
Ozzie PHP op 11/11/2013 22:37:42

Dan wordt er toch ook "onderwater" een method uitgevoerd die je niet ziet?

Is er zo'n methode nodig? Je kan het ook in de constructor doen zonder een andere methode er voor aan te roepen. Dus is het voor wat jij wilt doen nodig om zo'n methode te hebben?

Ozzie PHP op 11/11/2013 22:37:42

Stel we hebben een taart die door een class in stukken moet worden gesneden... wie moet dan de opdracht geven om de taart in stukken te snijden:

Hier geef ik de opdracht zelf:
<?php
$taart_snijder = new TaartSnijder($taart);
$taart_snijder->snijTaart();
$gesneden_taart = $taart_snijder->get();
?>
Maar ik kan ook de constructor de opdracht laten geven, en dan ziet de code er zo uit:

<?php
$taart_snijder = new TaartSnijder($taart);
$gesneden_taart = $taart_snijder->get();
?>
Beiden stukjes code doen hetzelfde, maar wat is de juiste manier? De taart moet altijd in stukken worden gesneden, dus zou ik denken dat de constructor hier opdracht voor mag geven... ???


Ik zou het beiden herschrijven naar iets als dit:
<?php

$taart = new AppelTaart(Vorm::CIRKEL, array(new Kaneel, new Krent, new Walnoot));
$vlaai = new Vlaai(Vorm::CIRKEL, array(new Kokos, new Chocolade));

$taartMes = new TaartMes(TaartMes::DUBBELZIJDIG); // kans van 1 op 384 op een SnijWondException

$taartStukken = $taartMes->verdeel($taart, 10); // tien gelijke stukken
$vlaaiStukken = $taartMes->verdeel($vlaai, array(1, 1, 2, 2, 2, 3, 3, 5)); 8 stukken met groote van $value/array_sum(array(1, 1, 2, 2, 2, 3, 3, 5))

?>
Telkens heb ik objecten waar ik meteen iets mee kan doen.
Ik begrijp dat als je "extra" dingen wilt doen, dat je dan meerdere methodes hebt. Maar als dat niet zo is, wat dan? Oké, laten we bij de taarten blijven :) Stel ik heb een class die de kersen UIT de taart haalt, jawel... dat is weer eens wat anders dan de kers op de taart!

Die class kan dus niks anders dan kersen uit de taart halen. Je kunt verder niks bepalen. Stop je dan de functie die de kersen uit de taart haalt in de constructor? Of moet je een method aanroepen?

Worden de kersen door (in opdracht van) de constructor verwijderd:

<?php
$kers_verwijderaar = new KersVerwijderaar($taart); // op dit moment worden de kersen via de constructor direct verwijderd
$kale_taart = $kers_verwijderaar->getTaart();
?>
of geef je de opdracht tot het verwijderen van de kersen zelf?

<?php
$kers_verwijderaar = new KersVerwijderaar($taart); // op dit moment zitten de kersen er nog op!
$kers_verwijderaar->verwijderKersen(); // nu pas worden de kersen verwijderd
$kale_taart = $kers_verwijderaar->getTaart();
?>
Ik ga proberen mijn vraag zo duidelijk mogelijk te formuleren. In het laatste voorbeeld zie je in de code dat de kersen worden verwijderd. Immers er staat $kers_verwijderaar->verwijderKersen(); Je zou dus kunnen zeggen dat deze code misschien duidelijker is dan het 1e voorbeeld waarin je dit niet kunt zien.

Echter... welk voorbeeld is logischer? Het enige wat de KersVerwijderaar class kan is kersen verwijderen, en dat kan ook maar 1x, want daarna zijn ze al verwijderd. Vanuit dat oogpunt is het dus logischer dat de constructor opdracht geeft om de kersen te verwijderen. Alleen je ziet dit dan niet terug in de code zoals in het 2e voorbeeld.

In dit specifieke voorbeeld, wat zou dan de juiste variant zijn. Ga je voor de logische variant (de 1e optie) die precies doet wat ie moet doen, of ga je voor de 2e variant, waarbij je eigenlijk onnodig een functie moet aanroepen, maar waardoor je wel weer duidelijker ziet wat er gebeurt?


Toevoeging op 12/11/2013 01:45:35:

Dos Moonen op 11/11/2013 21:59:38

Je kan autoloaders unregisteren om onnodige aanroepen naar die autoloader te voorkomen wanneer je voorbij een punt bent waar je die autoloader nodig had om library X correct te laten werken.
Stel je gebruikt een library om emails te versturen. De emails zijn verstuurd. Vervolgens ga je een library gebruiken om een form op te bouwen. Elk element wordt voorgesteld door zijn eigen class. Dat betekend veel calls naar de autoloaders. Waarom zou je de email library's autoloader nog laten zoeken? Wat is het nut? Het kost alleen maar tijd.

Nog even een vraag hierover he... als ik jou goed begrijp register en unregister jij dus regelmatig autoloaders? Van de ene kant snap ik wat je doet, maar van de andere kant vraag ik me ook af of je daarmee het nut van een autoloader niet teniet doet.

Een autoloader dient voor gemakt, zodat je ongestoord classes kunt aanroepen. Op de manier hoe jij het doet, moet je nu constant autoloaders gaan registeren en unregisteren. Maak je het jezelf dan niet heel lastig?

<?php
// register default autoloader
// code...
// Er treedt een Exception op..
// De exception moet gemaild worden...
// register de mail autoloader
// stuur een mail
// unregister de mail autoloader
// code...
// er moet een pdf gemaakt worden
// register de pdf autoloader
// maak een pdf
// het creëren van de pdf gaat mis
// Er treedt een Exception op..
// De exception moet gemaild worden...
// register de mail autoloader
// stuur een mail
// unregister de mail autoloader
// unregister de pdf autoloader
// log de Exception
// register de logger autoloader
// log de Exception
// unregister de logger autoloader
// enz.

?>
Lijkt me wat omslachtig...

Reageren