de OOP manier

Overzicht Reageren

Sponsored by: Vacatures door Monsterboard

Pagina: 1 2 3 volgende »

Ozzie PHP

Ozzie PHP

13/02/2014 00:54:41
Quote Anchor link
Hallo,

Ik heb weer even een OOP vraag.

Ik heb een container die services bevat. Heel simplistisch gesteld vraag ik aan de container een ID op, en dan krijg ik een (geconfigureerd) object terug. De verdere werking is voor mijn vraag niet echt relevant. Voorbeeldje:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
$mailer
= $container->get('mailer');
?>

Wat ik op dit moment doe is (zeer vereenvoudigd!) dit:

Ik heb een configuratie-bestand waar ik de ID's en de (geconfigureerde) objecten in zet:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
Foo
  class: Foo
  
Bar
  class: Bar
  parameters:
    foobar: true

Nogmaals, het is allemaal wat versimpeld, maar het gaat om het globale idee.

Ik laad het configuratie-bestand in als een array en sla de gegevens rechtstreeks op in een class property (array) van de container class. En dit is waar mijn vraag vooral over gaat. Ik sla de gegevens dus op in een class property, en als ik een service (object) nodig heb dan wordt deze aan de hand van de gegevens uit de array geconstrueerd.

Schematisch:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
$container->addConfig($config); // $config zijn de gegevens uit het config-bestand
$foo = $container->get('foo');

De vraag is of ik de gegevens uit het config-bestand direct in een class property (array) moet zetten, of dat het beter is om van ieder configuratie "item" een apart object te maken en deze aparte objecten op te slaan in een class property?

Ik kan me voorstellen dat het best lastig is om te volgen wat ik bedoel, maar ik hoop dat iemand het begrijpt.

Als ik de vraag heel plat stel, dan is eigenlijk de vraag: kan ik de array met configuratiegegevens in 1 keer in z'n totaliteit in een class property stoppen? Of is het de bedoeling dat ik van ieder element in de array een object maak, en dat ik al die afzonderlijke objecten toevoeg aan de class property in de container?

Nu doe ik zeg maar (weer even heel erg versimpeld) dit:

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
<?php
class Container {

  private $config;

  public function addConfig(array $config) {
    $this->config = array_merge($this->config, $config);
  }


  public function get($id) {
    $class = $this->config[$id]['class'];
    return new $class;
  }

}

?>

Is dit een goede manier? Of is eerder dit de bedoeling:

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
<?php
class Container {

  private $config;

  public function addConfig(array $config) {
    foreach ($config as $id => $service_config) {
      $this->config[$id] = new Service($service_config);
    }
  }


  public function get($id) {
    $class = $this->config[$id]->getClass();
    return new $class;
  }

}

?>

Het verschil zit 'm er dus in dat ik in de eerste situatie geen gebruik maak van afzonderlijke objecten (de informatie komt rechtstreeks uit de array), en in de 2e situatie wel.

Welke van deze 2 opties is de juiste OOP gedachtengang? Optie 1 of optie 2?
Gewijzigd op 13/02/2014 01:33:30 door Ozzie PHP
 
PHP hulp

PHP hulp

22/11/2024 04:08:24
 
Ward van der Put
Moderator

Ward van der Put

13/02/2014 08:53:13
Quote Anchor link
Aangezien één servicecontainer meerdere services bevat, is de tweede gedachtegang mooier OOP: een apart object voor de definitie van elke service.

In bijvoorbeeld Symfony is deze configuratie in YAML:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
# app/config/config.yml
services:
    my_mailer:
        class:        Acme\HelloBundle\Mailer
        arguments:    [sendmail]

gelijk aan deze configuratie in PHP:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php
// app/config/config.php
use Symfony\Component\DependencyInjection\Definition;

$container->setDefinition('my_mailer', new Definition(
    'Acme\HelloBundle\Mailer',
    array('sendmail')
));

?>
 
Ozzie PHP

Ozzie PHP

13/02/2014 12:49:02
Quote Anchor link
Ah oké. Dankjewel.

Ik had al zo'n vermoeden dat het beter OOP zou zijn om voor iedere service een apart object te gebruiken.

Stel nu dat je op een gegeven moment 500 services hebt, dan krijg je dus ook 500 objecten. Werkt dat niet vertragend? Of is dat verwaarloosbaar?
 
Ward van der Put
Moderator

Ward van der Put

13/02/2014 12:54:38
Quote Anchor link
Je neemt alleen kleine servicedefinities op in de servicecontainer, geen complete services. Waar jij een class Service hebt, heeft Symfony daarom een class Definition.

Pas bij de container->get('id') wordt het serviceobject van de gevraagde class gemaakt. Die vorm van "late binding" bindt resources dus pas aan de applicatie wanneer ze nodig zijn.

Je instantieert bijvoorbeeld de mailer-service pas als je gaat mailen. Tot die tijd is het slechts een kleine service-definitie in de servicecontainer.
 
Ozzie PHP

Ozzie PHP

13/02/2014 13:03:32
Quote Anchor link
Correct. Dat begrijp ik ook. De service (het object) wordt pas aangemaakt op het moment dat je het nodig hebt.

Maar ik bedoel wat anders. Ik laad dus de configuratiegegevens uit een bestand. Stel hier staan gegevens in voor 500 services. Wat ik momenteel doe, is eigenlijk klakkeloos deze gegevens (de array) in een class property (ook een array) van de container zetten.

De mooiere OOP oplossing is, zoals jij zelf aangaf, om de gegevens niet klakkeloos in een class property te zetten, maar om van iedere "definition" een apart object te maken.

Het mooie van dependency injection is dat er pas een object wordt aangemaakt, op het moment dat het nodig is. Maar aan de andere kant gaan we nu wel 500 objecten maken om de services te definiëren. Dus aan de ene kant van de weegschaal gaan we heel verstandig besparen (er wordt pas een object gemaakt als we het nodig hebben), maar aan de andere kant van de weegschaal maken we wel op voorhand alvast 500 objecten. Is dat wel handig/slim? En vooral... wordt je website hierdoor niet merkbaar trager? Of is dit verwaarloosbaar?
Gewijzigd op 13/02/2014 13:17:08 door Ozzie PHP
 
Mathieu Posthumus

Mathieu Posthumus

13/02/2014 13:15:16
Quote Anchor link
Ozzie wat is OOP dan?
 
Ozzie PHP

Ozzie PHP

13/02/2014 13:18:10
Quote Anchor link
Mathieu Posthumus op 13/02/2014 13:15:16:
Ozzie wat is OOP dan?

http://bit.ly/Y7sV6W
 
Wouter J

Wouter J

13/02/2014 13:23:36
Quote Anchor link
Dat is inderdaad praktisch verwaarloosbaar.




Reactie die ik vanochtend om half 9 schreef, maar toen niet verzond:

Optie 2 is zeker beter. Je hebt te maken met meer dan 1 setting: je hebt het id, de class, zijn argumenten (let op, niet zijn parameters!). Misschien ga je later nog tags toevoegen, etc. Dit is dus niet meer in een mooie manier vast te houden dan in een object en als je nieuwe dingen toevoegt wil je niet alles hoeven aanpassen, dan wil je alleen het Service object aanpassen.

Maar ik zou dit zelf niet in de Container afhandelen. Ik zou dit in de ContainerLoader afhandelen, de container moet alleen Service objects krijgen. De ContainerLoader laad de configuratie, zo heb je een YamlContainerLoader, etc. Deze configuratie zet hij vervolgens ook om in de juiste data, die hij vervolgens aan de container geeft. Abstract genomen:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?php
$ymlLoader
= new YamlFileContainerLoader($filelocator);
$xmlLoader = new XmlFileContainerLoader($filelocator);
$loader = new LoaderChain(array($ymlLoader, $xmlLoader));

$container = new Container();
$loader->load($container);
$compiledContainer = $container->compile();

// werk nu vervolgens met $compiledContainer
?>
 
Ozzie PHP

Ozzie PHP

13/02/2014 13:30:48
Quote Anchor link
Ik vind het altijd mooi om te zien hoe complex jij vaak met dingen werkt :) Het begin volg ik altijd nog, en dan ineens dan komt er iets ingewikkelds :)

In ieder geval dus gaan werken met losse objecten. Oké, duidelijk. Dat was het belangrijkste wat ik wilde weten.

>> Maar ik zou dit zelf niet in de Container afhandelen. Ik zou dit in de ContainerLoader afhandelen, de container moet alleen Service objects krijgen.

Je bedoelt eigenlijk te zeggen dat je de configuratie buiten de container inlaadt en de ingeladen gegevens aan de container geeft? Zoiets?
 
Ward van der Put
Moderator

Ward van der Put

13/02/2014 13:37:24
Quote Anchor link
Het is een drietrapsraket.

In de servicecontainer van de applicatie definieer je bijvoorbeeld alleen de mailer-service. Dat is één definitie.

Mogelijk heeft de mailer in tweede instantie, wanneer hij wordt geïnstantieerd, tientallen anderen objecten nodig voor het mailen. Dat laat je dan echter over aan de class loader van de mailer of een autoloader. Die klassen én de interne afhankelijkheden van de mailer-service blijven buiten de servicecontainer. De interne werking van een service is niet relevant voor applicaties die de service gebruiken.

Wat je inderdaad beter niet kunt doen, is alle 500 services van je platform alvast in de servicecontainer steken. Dat gevaar bestaat als je met slechts één config.yml of config.php werkt. Of met één index.php in de root die zogenaamd alles moet kunnen. Het werkt wel bij kleinschalige webprojecten, maar het schaalt niet lekker.

De servicecontainer gebruik je liever niet op siteniveau, maar op applicatieniveau. Hoeft een applicatie niet te mailen? Dan definieer je dus ook geen mailer-service in de servicecontainer van die applicatie.
 
Ozzie PHP

Ozzie PHP

13/02/2014 13:50:18
Quote Anchor link
>> De servicecontainer gebruik je liever niet op siteniveau, maar op applicatieniveau. Hoeft een applicatie niet te mailen? Dan definieer je dus ook geen mailer-service in de servicecontainer van die applicatie.

Ik snap je punt, maar dat is tegelijkertijd ook wel lastig. In iedere website moet je bijna wel kunnen mailen, weliswaar niet bij iedere pagina-aanroep. Het mailen is (hoe ik het zie) een basis-functionaliteit. Als ik die niet zou opnemen op siteniveau, dan moet ik iedere keer als ik de mailer wil gebruiken, eerst de mailer definition/config gaan inladen. Dat lijkt me eigenlijk ook niet helemaal de bedoeling toch? Wel zou ik me kunnen voorstellen dat je bij een webshop een winkelwagen service nodig hebt, die je bij een normale site niet nodig hebt.
 
Mathieu Posthumus

Mathieu Posthumus

13/02/2014 13:53:45
Quote Anchor link
Ozzie PHP op 13/02/2014 13:18:10:
Mathieu Posthumus op 13/02/2014 13:15:16:
Ozzie wat is OOP dan?

http://bit.ly/Y7sV6W


thx hahaha
 
Wouter J

Wouter J

13/02/2014 14:29:13
Quote Anchor link
>> Je bedoelt eigenlijk te zeggen dat je de configuratie buiten de container inlaadt en de ingeladen gegevens aan de container geeft? Zoiets?

Zoiets ja :)

Een loader laad de configuratie, dat is namelijk niet de taak van de container. De loader krijgt vervolgens de container instance en zet daarin de verkregen services.

>> Als ik die niet zou opnemen op siteniveau, dan moet ik iedere keer als ik de mailer wil gebruiken, eerst de mailer definition/config gaan inladen. Dat lijkt me eigenlijk ook niet helemaal de bedoeling toch? Wel zou ik me kunnen voorstellen dat je bij een webshop een winkelwagen service nodig hebt, die je bij een normale site niet nodig hebt.

Doormiddel van configuratie moet je kunnen bepalen of je een service inlaad. Dummy code:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
if (config.ozzie_framework.mailer) {
    container.load('mailer_configuration')
}
 
Ozzie PHP

Ozzie PHP

13/02/2014 15:31:49
Quote Anchor link
>> Een loader laad de configuratie, dat is namelijk niet de taak van de container. De loader krijgt vervolgens de container instance en zet daarin de verkregen services.

Huh... dit volg ik ff niet :) Heb je het nu over een algemene loader, of is dit specifiek een loader voor de services container? En waarom geef je de container aan de loader ipv de gegevens uit de loader aan de container?

>> Doormiddel van configuratie moet je kunnen bepalen of je een service inlaad. Dummy code:

Dit snap ik ook even niet. Ik zet al mijn configuraties in 1 (YAML) bestand. Je kunt toch niet per service zeggen of ie wel of niet beschikbaar moet zijn?
 
Wouter J

Wouter J

13/02/2014 15:54:29
Quote Anchor link
>> Huh... dit volg ik ff niet :) Heb je het nu over een algemene loader, of is dit specifiek een loader voor de services container? En waarom geef je de container aan de loader ipv de gegevens uit de loader aan de container?

Een service configuratie ziet er compleet anders uit als een routing configuratie, elke configuratie heeft dus recht op zijn eigen loader.

>> Dit snap ik ook even niet. Ik zet al mijn configuraties in 1 (YAML) bestand. Je kunt toch niet per service zeggen of ie wel of niet beschikbaar moet zijn?

Niet alles in 1 yaml bestand te zetten. Zet alle mailer specifieke services/parameters in mailer.yml, alle services/parameters voor het winkelwagentje in shop.yml, etc. Vervolgens kun je bepalen welke bestanden je wel en welke je niet inlaad.
 
Ozzie PHP

Ozzie PHP

13/02/2014 16:02:48
Quote Anchor link
>> Een service configuratie ziet er compleet anders uit als een routing configuratie, elke configuratie heeft dus recht op zijn eigen loader.

Oké. Ik denk dat ik het snap. Waarom heb jij in jouw voorbeeld meerdere types loader (yaml en xml)? Je schrijft je services toch in 1 formaat (een yaml bestand)?

>> Niet alles in 1 yaml bestand te zetten. Zet alle mailer specifieke services/parameters in mailer.yml, alle services/parameters voor het winkelwagentje in shop.yml, etc.

Oké, als ik je goed begrijp krijg je dan dus een bestand met "defaults" en andere bestanden met bijv. een mailer, shop e.d. Maar stel nu dat jij gemaild wilt worden als er een exception optreedt... zet je die mailer service dan in een apart bestand, of is die mailer service een onderdeel van de default services?
 
Wouter J

Wouter J

13/02/2014 16:10:52
Quote Anchor link
>> Waarom heb jij in jouw voorbeeld meerdere types loader (yaml en xml)? Je schrijft je services toch in 1 formaat (een yaml bestand)?

Een old habit van Symfony2. Symfony2 staat toe om je configuratie in 3 formaten te schrijven: yaml, xml en php. Dat neem ik eigenlijk altijd over in mijn libraries...

>> Oké, als ik je goed begrijp krijg je dan dus een bestand met "defaults" en andere bestanden met bijv. een mailer, shop e.d. Maar stel nu dat jij gemaild wilt worden als er een exception optreedt... zet je die mailer service dan in een apart bestand, of is die mailer service een onderdeel van de default services?

Ik zou geen bestand default services maken. Gewoon alles opsplitten in bestanden en dan de bestanden met services die je altijd nog hebt inladen en vervolgens nog dynamisch. Daarna cache je de container, zodat dit totaal geen snelheidsverlies oplevert.
 
Ward van der Put
Moderator

Ward van der Put

13/02/2014 16:13:26
Quote Anchor link
>> Maar stel nu dat jij gemaild wilt worden als er een exception optreedt... zet je die mailer service dan in een apart bestand, of is die mailer service een onderdeel van de default services?

Exceptions mail je niet, maar log je. De configuratie van de logger bepaalt vervolgens hoe en waar er wordt gelogd: per e-mail bijvoorbeeld, of anders in een logbestand of een database.
 
Ozzie PHP

Ozzie PHP

13/02/2014 16:35:00
Quote Anchor link
>> Ik zou geen bestand default services maken. Gewoon alles opsplitten in bestanden en dan de bestanden met services die je altijd nog hebt inladen en vervolgens nog dynamisch.

Ho... niet zo snel ;)

Je bedoelt dus dingen die bij elkaar horen/met elkaar te maken hebben zet je in aparte bestanden. En de dingen die je altijd nodig hebt zet je in 1 bestand, en dit bestand laad je in en dit cache je dan toch? De dynamische kun je toch niet mee-cachen? (omdat ze dynamisch zijn)

>> Exceptions mail je niet, maar log je. De configuratie van de logger bepaalt vervolgens hoe en waar er wordt gelogd: per e-mail bijvoorbeeld, of anders in een logbestand of een database.

Correct... ik doelde meer op het feit dat je in alle applicaties een mailer nodig kunt hebben omdat er mogelijk een mail moet worden verstuurd. Mijn vraag: laad je die mailer dan standaard in in je service container? Of ga je die pas inladen op het moment dat er daadwerkelijk iets gemaild moet worden. Anders gezegd, moet ik de mailer telkens "inschakelen" op het moment dat ik 'm nodig heb?
 
Ward van der Put
Moderator

Ward van der Put

13/02/2014 17:08:33
Quote Anchor link
>> Correct... ik doelde meer op het feit dat je in alle applicaties een mailer nodig kunt hebben omdat er mogelijk een mail moet worden verstuurd.

Je draait dan steeds om dezelfde hete brij heen: je gaat alle "mogelijke" services laden omdat ze "mogelijk" nodig zijn. Dat is zoals zogenaamd op survival gaan en dan stiekem je hele huisraad inclusief breedbeeld-tv en combimagnetron in een caravan meesleuren, want "je weet maar nooit wanneer je het nodig hebt".

Probeer concrete applicaties eens wat meer los te denken van je klassen. Klassen kunnen redelijk autonoom van alles en nog wat doen, omdat ze los van een context werken. Applicaties moeten echter gericht concrete taken uitvoeren; dat doen ze door de klassen aan het werk te zetten met een voor hun toepassing relevante configuratie. Vereenvoudigd gezegd is de configuratie dus de lijm tussen redelijk abstracte klassen en het gebruik van die klassen in een concrete applicatie.

Neem bijvoorbeeld opnieuw de mailer-service. Die kan van alles en nog wat mailen, maar niet elke applicatie wil hetzelfde type mail op dezelfde wijze mailen. De specifieke applicatie bepaalt daarom hoe er concreet wordt gemaild, niet een of andere standaardservice die door alle applicaties wordt gedeeld.
 
Ozzie PHP

Ozzie PHP

13/02/2014 17:51:12
Quote Anchor link
Ward, ik begrijp ook wel wat je bedoelt. Per applicatie andere services. Tot zover volg ik je. Maar binnen een applicatie, ga je dan een service, bijv. een mailer, pas laden op het moment dat je die daadwerkelijk nodig hebt? Of, laad je hem sowieso, omdat je weet dat je hem nodig zou kunnen hebben. Snap je het verschil tussen beide situaties?
 

Pagina: 1 2 3 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.