static method maakt nieuw object?

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: 1 2 volgende »

Ozzie PHP

Ozzie PHP

12/06/2014 08:16:13
Quote Anchor link
Hey guys,

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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php

  public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) {
    $spyc = new Spyc;
    return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes);
  }



?>

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?
 
PHP hulp

PHP hulp

25/12/2024 14:02:13
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 09:30:35
Quote Anchor link
Dit type self-creation pattern is een goedkoop/zuinig alternatief voor een externe factory of builder. Als je alleen maar een Foo nodig hebt en die Foo al volledig beschreven wordt door de class Foo zelf, dan kun je de Foo-klasse zelf een Foo-object laten maken, zonder tussenkomst van een FooFactory of een FooBuilder.

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 12:02:48
Quote Anchor link
Maar Ward, ik snap het nut niet :-s

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?
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 12:14:57
Quote Anchor link
Het hangt ervan af. Het voegt inderdaad niets toe als je een standaard pattern gebruikt:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$foo
= new Foo();
?>


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:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$foo
= FooFactory::create();
?>


Stel nu dat we hiervoor deze opzet hebben gebruikt:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class Foo
{
}


class FooFactory
{
    public static function create()
    {

        return new Foo();
    }
}

?>


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:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php
class Foo
{
    public static function create()
    {

        return new Foo();
    }
}

?>


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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class User {}

class SuperUser extends User {}

class UserFactory
{
    public static function create($type = null)
    {

        if ($type == 'admin' || $type == 'root') {
            return new SuperUser();
        }
else {
            return new User();
        }
    }
}

?>
Gewijzigd op 12/06/2014 12:15:42 door Ward van der Put
 
Ozzie PHP

Ozzie PHP

12/06/2014 12:23:46
Quote Anchor link
Oké... maar mijn vraag is vooral, waarom zou je zoiets als dit willen doen?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php
class Foo
{
    public static function create()
    {

        return new Foo();
    }
}

?>

Ik heb dus een Foo nodig. Waarom zou ik dan dit willen doen:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$foo
= Foo::create();
?>

In plaats van

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$foo
= new Foo();
?>

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
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 12:55:03
Quote Anchor link
Je zou dat kunnen doen om dezelfde reden als waarom je een factory of builder gebruikt: je kunt de methode create() andere/meer dingen laten doen. In dat geval zou ik dus wél een aparte klasse toevoegen.

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 12:59:18
Quote Anchor link
En onder welke categorie valt volgens jou dit geval? De laatste?

Ik vind het maar vreemd...
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 13:16:15
Quote Anchor link
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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 13:20:44
Quote Anchor link
Hier de link: https://github.com/mustangostang/spyc/blob/master/Spyc.php

Ben benieuwd of je er iets zinnigs over kunt zeggen...
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 13:34:18
Quote Anchor link
Het begint met drie keer if (!function_exists(...)) en de public static function wordt daarna ook niet geschuwd, dus dat riekt inderdaad naar een herschreven function library.

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 13:41:16
Quote Anchor link
Ja, daar ben ik nog niet helemaal over uit. Er staan best goede berichten namelijk over Spyc. En het is maar 1 bestand wat op zich ook wel fijn is.

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.
 
Wouter J

Wouter J

12/06/2014 13:48:55
Quote Anchor link
>> 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.

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 13:55:18
Quote Anchor link
>> 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.

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)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
$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.
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 13:58:19
Quote Anchor link
>> 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.

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.
 
Dos Moonen

Dos Moonen

12/06/2014 14:05:25
Quote Anchor link
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
 
Ozzie PHP

Ozzie PHP

12/06/2014 14:16:11
Quote Anchor link
@Dos

>> 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.

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?

Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
<?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...

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
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 14:30:22
Quote Anchor link
Nee, ik zeg niet iets anders. Je kunt in /library/ een universele autoloader plaatsen die moeiteloos /library/vendor-foo/ en /library/vendor-bar/ kan laden. Zit daar nog een namespace-loos bouwsel met de verouderde vendor_package_class-syntaxis tussen, dan werkt dat bij PSR-0 ook.

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 14:36:27
Quote Anchor link
Maar Ward, dat verhaal van pas registeren op het moment als je iets nodig hebt en weer unregisteren als je het niet meer nodig hebt... wat moet je daar dan mee? Is dat nog van toepassing? Of kan ik beter gewoon alle autoloaders direct in de bootstrap registeren??? En is een unregister method in een autoloader dan ook zinloos?

>> 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.
 
Ward van der Put
Moderator

Ward van der Put

12/06/2014 14:54:36
Quote Anchor link
Ik heb in de bootstrap meestal één autoloader als anonymous function die alles uit de gehele library kan laden. Die krijgt via $prepend ook voorrang voor andere autoloaders, zodat er niet stiekem nog wordt gezocht in een include path en naamconflicten zijn uitgesloten.

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 :)
 
Wouter J

Wouter J

12/06/2014 15:03:49
Quote Anchor link
Ozzie, leg mij dan eens uit wat het nut is van autoloaders in dit voorbeeldje dan:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
<?php
YamlParserAutoloader::register();
$parser = new YamlParser();
YamlparserAutoloader::unregister();
?>

Waarom doe je dan niet meteen
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
<?php
require_once 'YamlParser.php';
$parser = new YamlParser();
?>


??

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.
 
Ozzie PHP

Ozzie PHP

12/06/2014 15:14:40
Quote Anchor link
Wouter, dat is dus juist waarom ik zo kritisch ben op dit soort dingen. Ik vond het zelf ook raar om autoloaders te gaan registeren en dan weer unregisteren, maar Dos en Ward geven aan dat dat gewoon voorkomt.

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
 

Pagina: 1 2 volgende »



Overzicht Reageren

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.