static method maakt nieuw object?
Ik zat even in de code te kijken van een Yaml parser (Spyc) en daarin zie ik een paar static methods die vervolgens een nieuw object van diezelfde class aanmaken:
Code (php)
Kan iemand uitleggen waarom ie een nieuw object aanmaakt? Het nut van een static method is toch juist dat je niet iedere keer een nieuw object hoeft aan te maken?
Logischerwijs moet die methode in class Foo zelf dan static zijn, want je wilt daarmee een new Foo kunnen maken als er nog geen Foo-object is.
Je kunt nu dus dit doen:
Spyc::YAMLDump();
En onderwater wordt dan een nieuw Spyc aangemaakt. Dat slaat toch nergens op?
Dat is hetzelfde als je dit zou doen:
$spyc = new Spyc();
$spyc->YamlDump();
Wat is in dit geval dan het nut of de toegevoegde waarde om een statische functie te gebruiken als onderwater toch stiekem een nieuw object wordt aangemaakt?
Je kunt dit een prototype pattern noemen als de class Foo() op eigen kracht een volwaardige Foo oplevert.
De zaak verandert als je een ander creational pattern gebruikt, bijvoorbeeld een factory pattern:
Stel nu dat we hiervoor deze opzet hebben gebruikt:
Code (php)
Dan valt op dat de factory betrekkelijk weinig verantwoordelijkheden heeft: het factory pattern geeft nog steeds een object terug volgens een prototype pattern.
In dat geval kun je daarop bezuinigen (bijvoorbeeld in een extra round-trip naar het file system via een autoloader) door beide in elkaar te schuiven:
Nu hebben we dus een mengvorm van het prototype pattern en het factory pattern. Dat is wat jij in die code aantreft.
Dit kán een anti-pattern worden, met name wanneer de factory wél aanvullende verantwoordelijkheden heeft. Bijvoorbeeld in het volgende geval kunnen we het geheel niet meer zomaar "samenvouwen":
Code (php)
Gewijzigd op 12/06/2014 12:15:42 door Ward van der Put
Ik heb dus een Foo nodig. Waarom zou ik dan dit willen doen:
In plaats van
Daarbij komt dat in de method uit het 1e berichtj er ook geen object wordt teruggegeven. Ik voer dus een statische functie uit. Handig zou je zeggen, want dan hoeft er geen object te worden aangemaakt (gunstig voor het geheugen). Maar onderwater wordt er gewoon wel een object aangemaakt. Stel ik moet bijv. 10 bestanden inladen, dan wordt er 10x een object aangemaakt. Misschien zie ik iets over het hoofd, maar ik begrijp die hele constructie niet eigenlijk... :-s
Bovendien kun je in een constructor niet toereikend exceptions afhandelen, waardoor een directe aanroep van een complexe new Foo() zich minder makkelijk laat aanpassen en testen dan een omweg langs Foo::create().
Verder kun je in het wild nog een luie en onelegante reden aantreffen: één grote klasse die op eigen kracht al alles kan (en waarschijnlijk te veel doet), waardoor de maker het nut van een aparte factory of builder niet inzag.
Tot slot zou het nog zoiets kunnen zijn als je bij aliassen en updates vaak ziet: hetzelfde op twee verschillende manieren doen, omdat de één "zus" en de ander "zo" handiger vindt of omdat we iets vroeger "linksom" deden en dat nu "rechtsom" doen.
Ik vind het maar vreemd...
Ik ken de parser niet (linkje?), maar het zou ook nog een "legacy support"-erfenisje kunnen zijn van functioneel PHP dat naar OOP is herschreven. Daarin worden relatief vaak statische methoden gebruikt, omdat die zich als functies laten aanroepen.
https://github.com/mustangostang/spyc/blob/master/Spyc.php
Ben benieuwd of je er iets zinnigs over kunt zeggen...
Hier de link: Ben benieuwd of je er iets zinnigs over kunt zeggen...
Dat zegt — overigens — verder niets over de kwaliteit van de oplossing, maar aangezien je YAML wilt gebruiken voor de configuratie van de kernel en de applicaties in een OOP-framework, lijkt bijvoorbeeld Symfony\Component\Yaml me een veel eleganter voorbeeld.
Ik heb die yaml reader straks niet bij ieder request nodig, dus ik wil 'm pas inladen op het moment dat ik 'm nodig heb. Bij spyc hoef ik alleen een bestand te includen. Bij de symfony variant moet ik een autloader variant inschakelen. Ik weet niet eens of het een psr-0 of psr-4 autoloader gebruikt. En als je het efficiënt wil doen, dan zou je telkens voordat je de yaml reader gebruikt de autoloader moeten registeren, en als je klaar bent weer unregisteren. bij spyc is dat niet van toepassing. Dat vind ik wel een voordeel.
Dan is het hele nut van die autoloader weg. Laat autoloader configuratie gewoon in je bootstrap en denk de rest van je applicatie niet meer over die autoloader na.
Trouwens zal je composer moeten gaan gebruiken voor third party software en raad ik je nogmaals aan om ook deze autoloader te gebruiken. Dan ben je helemaal klaar en heb je geen ruzie met het autoloaden.
Een library kiezen omdat ie maar 1 bestand is is naar mijn mening een bad practise.
Wouter... wat bedoel jij dan? Gewoon altijd een autoloader instellen, en niet pas op het moment dat je de Yaml Reader nodig hebt? Ik meen eigenlijk dat eerder op het forum juist gezegd werd dat je een autloader voor third-party software pas moet inschakelen als je het nodig hebt, en daarna weer moet uitschakelen. Bijvoorbeeld:
Code (php)
1
2
3
4
5
2
3
4
5
<?php
$this->container->get('pdf_autoloader')->register();
// doe iets met PDFjes
$this->container->get('pdf_autoloader')->unregister();
?>
$this->container->get('pdf_autoloader')->register();
// doe iets met PDFjes
$this->container->get('pdf_autoloader')->unregister();
?>
Is dit niet de bedoeling dan?
Op welke manier autoloadt Symofony, via psr-4 standaard?
>> Een library kiezen omdat ie maar 1 bestand is is naar mijn mening een bad practise.
Daar heb je wel gelijk in. Ik ben er alleen nog niet over uit welke yaml parser het beste is.
Dan zou ik sowieso een autoloader gebruiken. Het voordeel daarvan is nu juist precies dat iets alleen wordt geladen wanneer het nodig is.
>> En als je het efficiënt wil doen, dan zou je telkens voordat je de yaml reader gebruikt de autoloader moeten registeren, en als je klaar bent weer unregisteren. bij spyc is dat niet van toepassing.
Naar alle waarschijnlijkheid heb je die autoloader toch altijd nodig, voor bijvoorbeeld de Kernel- en Request-klassen, dus die overhead gebruik je toch al. Dan is dat geen issue meer.
Wat wel een issue is, is dat je met een eenmaal geladen autoloader die autoloader vervolgens niet gebruikt omdat je een functiebibliotheek wilt laden die niet PSR-0 of PSR-4 compliant is. Als je naast een loader nog andere loaders nodig hebt — of loaders die loaders loaden — krijg je een instabiel kaartenhuis. Doe het dan liever meteen goed: één autoloader die de structuur van je /library/ doorziet en daaronder via namespaces alles kan vinden.
Met een simpele PSR-0 autoloader kan dat overigens ook al. Ik gebruik zelf vaak een licht verbeterde versie van het standaardvoorbeeld uit PSR-0.
Quote:
Ik meen eigenlijk dat eerder op het forum juist gezegd werd dat je een autloader voor third-party software pas moet inschakelen als je het nodig hebt, en daarna weer moet uitschakelen.
Nee, dat zei ik in de context dat als je gaat mieren neuken over dingen als "wat is sneller, ++$i of $i++ wanneer je het in een loop gebruikt?" dat je dan een hypocriet bent als je autoloaders niet unload nadat een library geladen is omdat het daarna dingen onnodig vertraagt.
Je hebt nu een deel van de uitleg onthouden terwijl de boodschap die je had moeten onthouden tussen de regels door te lezen is: micro optimization is the root of all evil.
Gewijzigd op 12/06/2014 14:05:57 door Dos Moonen
>> Je hebt nu een deel van de uitleg onthouden terwijl de boodschap die je had moeten onthouden tussen de regels door te lezen is: micro optimization is the root of all evil.
Dat vind ik eerlijk gezegd wel een opmerkelijke uitspraak, want zo kwam het destijds niet over. Ik heb nog even jouw uitleg erbij gepakt:
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.
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.
Vervolgens mijn reactie hierop:
Ozzie PHP op 11/11/2013 23:50:43:
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?
Lijkt me wat omslachtig...
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?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?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.
?>
// 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...
En Ward reageert hier dan weer als volgt op:
Ward van der Put op 12/11/2013 08:46:17:
Zo omslachtig is dat soms inderdaad. Verwerk maar eens een bestelling voor een webwinkel. Een geslaagde betaling (namespace betaalprovider) leidt tot het mailen (namespace mailer) van een PDF (namespace PDF-framework) en een order (namespace fulfilment) die je meteen inschiet bij de vervoerder (PostNL namespace). En ondertussen moet je eigen framework nog iets fatsoenlijks op het scherm van de klant toveren...
En nu een half jaar later zeggen jullie ineens iets anders :-s
Je kunt dat ongestraft doen mits je het geheel uitgebreid test en mits je aangetroffen bugs zelf verhelpt. Ik sta er dus anders in: ik ga er bij voorbaat van uit dat even iets in een eigen directory unzippen alleen nooit voldoende zal zijn.
Verder ben ik ook niet vies van micro-optimalisaties hoor. Bijvoorbeeld een file_exists() hoort niet thuis in een autoloader: als een vereist bestand ontbreekt, hoort dat te eindigen in een fatale fout, daarvoor hoef ik niet via het besturingssysteem nog even aan te kloppen bij het file system.
>> Bijvoorbeeld een file_exists() hoort niet thuis in een autoloader: als een vereist bestand ontbreekt, hoort dat te eindigen in een fatale fout...
Ik heb het er wel in zitten. Ik heb het eens getest en het scheelt qua tijd praktisch niks. Ik vind het wel prettig als ik via m'n eigen logger/mailer gewaarschuwd wordt als een bestand ontbreekt. Lijkt me niet fijn als door bijv. een beschadigd bestand je applicatie niet meer werkt zonder dat je daar zelf van op de hoogte wordt gebracht. Daarmee neem je een bewust risico lijkt me.
Een autoloader unregisteren heb ik zelf nog nooit nodig gehad, dus dat laat ik voor wat het is. Wel gebruik/misbruik ik nog wel eens een destructor om niet-kritieke taken uit te stellen.
Verder zet de bootstrap elke PHP-fout om in een ErrorException, zodat die kan worden gelogd. Kritieke exceptions worden inderdaad gemaild naar een Gmail-account, zodat ikzelf of een admin kan ingrijpen. Een mislukte require blijft dus nooit onopgemerkt.
Persoonlijk vind ik dat je daarmee altijd productie moet durven draaien met error reporting op E_ALL en E_STRICT en een leeg Postvak IN voor exceptions. Dan heb je je werk goed gedaan, anders niet :)
Code (php)
1
2
3
4
5
2
3
4
5
<?php
YamlParserAutoloader::register();
$parser = new YamlParser();
YamlparserAutoloader::unregister();
?>
YamlParserAutoloader::register();
$parser = new YamlParser();
YamlparserAutoloader::unregister();
?>
Waarom doe je dan niet meteen
??
Het is slecht om zomaar de mening van andere over te nemen zonder er zelf eerst even bij stil te hebben gestaan en gedacht te hebben: Wat is dat nou weer voor een idioot idee.
Jouw voorbeeld met 1 class zou ik geen autoloader voor gebruiken, maar stel het is een component zoals bij Symfony, dan gaat het om meerdere classes en dan is een autoloader handig.
Het idee van het registeren op het moment dat je het nodig hebt en onregistreren als je het niet meer nodig hebt was, als ik het van de heren goed heb begrepen, dat je minder actieve autoloaders hebt wat beter is voor de performance. Maar goed, dat verhaal wordt nu dus min of meer ontkracht begrijp ik.
Gebruik jij trouwens zelf de Yaml component van Symfony?
Gewijzigd op 12/06/2014 15:15:04 door Ozzie PHP