Hoi, ik werk aan een project waarin ik een class heb gemaakt die api calls doet. Dit haalt producten op, maar kan ook producten/klanten etc opslaan.
Ook heb ik een Cart class gemaakt. Ik vind het handig om alle producten zo bij te houden en het totaal bedrag te bereken. De Class methodes worden uitgevoerd via ajax calls, dus de product ids komen vanuit de voorkant meegestuurd via JS. Nu is het zo dat ik nog het id, product naam en prijs meestuur naar de Cart. Maar wil eigenlijk alleen nog maar het id meesturen, om dat de gebruiker daar weinig mee kan saboteren. Ik weet wel hoe ik de data enzo kan ophalen, maar vind het slordig om direct die api call functies van die api class direct in mijn Cart class te gebruiken.
Is het overigens ook handig om per product property een class te maken, of is dat iets te veel van het goede? Vind het zelf niet nodig, maar hoorde dat van iemand.
Ja, dat noemen we value objects (of ook wel data objects):
Dat merk je vanzelf wanneer je bijvoorbeeld niet alleen bedragen in euro's maar ook bedragen in Britse ponden, Zwitserse franken of Amerikaanse dollars moet verwerken. Je hebt dan niet meer genoeg aan alleen een bedrag, maar moet ook overal de valuta weten. Dat is één samenhangend geheel.
Een persoonsnaam is een ander, misschien wat ingewikkelder voorbeeld. Ik raak al meer dan 50 jaar de "van der" in mijn achternaam kwijt. En dat bij hele grote bedrijven waarvan je wel meer verwacht: dit jaar was het raak/mis bij ING en KPN.
Het probleem is onder andere dat een systeem dat alleen een voornaam plus achternaam verwerkt moet samenwerken met een systeem dat de drie-eenheid voornaam, tussenvoegsels en achternaam gebruikt. En dan heb je ook nog Amerikaanse systemen waarin de middle name (de F. in John F. Kennedy) wordt misbruikt voor Nederlandse tussenvoegsels.
Dit soort problemen kun je voorkomen (en oplossen) door de verantwoordelijkheid bij één class Name te leggen. Andere classes hoeven dan geen onderscheid meer te maken tussen allerlei soort namen en onderdelen van namen: ze gebruiken alleen de class Name.
Ah oke klinkt logisch, in mijn geval heb ik voor een product het bedrag in euros genoeg aan. Dus dat hoeft niet echt complex te zijn, maar ik snap inderdaad wat je bedoelt.
Ward legt het keurig uit. Het hangt wel af van de complexiteit van je systeem, en van de complexiteit van een functie. Als je gewoon een vaste waarde ophaalt, dan hoef je daar niet per se een aparte class voor te maken.
Bijv. je moet een intern productnummer (sku) ophalen. Dat kan gewoon als volgt:
<?php
public function getSku(): string {
return $this->sku;
}
?>
Dat kan ook vaak met een productnaam. Wat Ward als voorbeeld geeft van iemands naam kan in een aparte class, mits er een bepaalde vorm van complexiteit met die functie samenhangt. En dan hangt het ook nog af van de mate van complexiteit. Bijv. stel je wilt dat ieder woord met een hoofdletter begint. Als dat het enige is, dan zou ik daar geen aparte class voor maken.
Ander voorbeeld: stel een user heeft een username, dat is dan gewoon een string, net als hierboven in het voorbeeld van de sku. Daar hoef je geen aparte class voor te maken.
Het is een beetje je eigen keuze. OOP geeft richtlijnen, maar het zijn geen verplichtingen. Je moet vooral voor jezelf kunnen verantwoorden waarom je iets op een bepaalde manier doet, en daar moet je consequent (belangrijk) in zijn.
Een website voor de kruidenier op de hoek, richt zich op de lokale markt en kan daarom om andere vereisten en benodigdheden vragen, dan een commercieel framework dat zich richt op (inter)nationale transacties met verschillende valuta.
Werk je in je eentje of in een team aan de website/webshop? Werk je er alleen aan, dan bepaal jij de spelregels. Werk je met meerdere mensen eraan, dan zul je spelregels moeten opleggen waar iedereen zich aan houdt.
Het gebruiken van een class voor iedere property kan ook zo'n spelregel (standaard) zijn. Niet omdat het per se altijd noodzakelijk is, maar omdat het consistent is en iedereen dan weet dat een property wordt opgehaald via een class.
Kortom, doe wat in jouw situatie nuttig is. Zorg er vooral voor dat je je keuzes kunt verantwoorden en dat je consistent bent in je werkwijze.
Hoop dat je iets aan deze extra toevoeging hebt ;-)
Yes! Bedankt voor de uitleg, het is nu wel een stuk duidelijker en het hangt dus gewoon echt af van de situatie. Voor nu zijn mijn vragen door jullie heel netjes beantwoord en neem ik de adviezen in de toekomst zeker mee. Want ik ga echt wel een keer in een situatie belanden, waar een property een heel object moet zijn met extra functionaliteit.
Graag gedaan! Als laatste advies zou ik je willen meegeven om gewoon over alles wat je doet de eerste keer goed na te denken. Als je er 1 keer heel goed over nadenkt, dan kun je de voor jou meest geschikte werkwijze kiezen en kun je die alle volgende keren opnieuw gebruiken (zonder dat je er dan weer opnieuw over moet gaan nadenken).
Dat kan over alles gaan. Bijvoorbeeld ter inspiratie: dit topic. Wat is je return waarde?
Maar zo zijn er heel veel dingen waar je over kunt nadenken. En het is goed om dat 1 keer heel goed te doen, zodat je daarop volgende code consistent is.
Een ander voorbeeld.
<?php
class {
public function foo(): ?array {
// code
}
}
?>
versus
<?php
class
{
public function foo() : array|null
{
// code
}
}
?>
Allebei helemaal prima, maar merk op dat de posities van de accolades en de dubbele punt anders is. Net als array|null versus ?array.
Er staat in beide gevallen precies hetzelfde, maar de code is anders 'opgemaakt'. Ook daar zijn standaarden voor, maar het voornaamste is dat je iets kiest, en dat consistent doorvoert. Als je dat doet dan heb je voor jezelf (of voor je team) altijd prettig leesbare code.
Voor codes en nummers die aan bepaalde eisen moeten voldoen (syntaxis, opmaak, lengte, enzovoort), zou ik dus wél een value object gebruiken. Denk bijvoorbeeld aan EAN-nummers, BSN-nummers en IBAN-nummers.
Maar omdat het vooral een ontwerpkeuze is, zal ik ook uitleggen waarom ik dat doe.
Stel, je doet inderdaad zoiets:
<?php
public function setSku(string $sku): void
{
$this->sku = $sku;
}
public function getSku(): string
{
return $this->sku;
}
?>
Als een business case (of de baas) vereist dat de SKU's worden aangepast, dan moet je overal die strings en stringfuncties aanpassen. Had je nou voor SKU's echter een aparte class StockKeepingUnit gebruikt, dan hoeft dat niet: je past alleen die ene class StockKeepingUnit aan en de rest van de code werkt als vanouds:
<?php
public function setSku(StockKeepingUnit $sku): void
{
$this->sku = $sku;
}
public function getSku(): StockKeepingUnit
{
return $this->sku;
}
?>
Een value object zelf is zelden heel complex. Ik maak ze gemakshalve meestal stringable, zodat je ze kunt echoën in de juiste syntaxis of je ze in een SQL-query kunt steken, bij een SKU bijvoorbeeld een "WHERE product_id = " . $sku.
<?php
declare(strict_types=1);
class StockKeepingUnit implements \Stringable
{
private readonly string $value;
public function __construct(mixed $value)
{
// ... Hier kun je eventueel nog allerlei validaties kwijt. ...
if (empty($value)) throw new \ValueError();
$this->value = (string) $value;
}
public function __toString(): string
{
return $this->value;
}
}
?>
Voor codes en nummers die aan bepaalde eisen moeten voldoen (syntaxis, opmaak, lengte, enzovoort), zou ik dus wél een value object gebruiken. Denk bijvoorbeeld aan EAN-nummers, BSN-nummers en IBAN-nummers.
Daar kan ik me inderdaad wat bij voorstellen. Daar zit een zekere mate van complexiteit in, en je kunt het op meerdere plekken nodig hebben. In zo'n geval is dat zeker een goed idee.
?
Onbekende gebruiker
22-12-2022 21:13
gewijzigd op 22-12-2022 21:17
Ward van der Put op 21/12/2022 11:17:37
[quote="Jorn Reed op 21/12/2022 09:20:21"]
Is het overigens ook handig om per product property een class te maken, of is dat iets te veel van het goede? Vind het zelf niet nodig, maar hoorde dat van iemand.
Ja, dat noemen we value objects (of ook wel data objects):
Ik gebruik ze zelf zodra je twee of meer dingen hebt die bij elkaar horen. Martin Fowler noemt als voorbeeld de x en y in coördinaten.
[/quote]
Dit is een klassiek voorbeeld van hoe OOP kan worden ingezet. Maar dat is niet de meerwaarde van OOP. Want waarom zou je code schrijven die alleen maar data vast houdt? Dat geeft alleen maar overhead.
In plaats daarvan kan je ook dezelfde functies schrijven waar geen geïnstantieerd object omheen zit dat extra geheugen nodig heeft, dat geeft efficiëntere (snellere) code.
Nou is het wel zo dat PHP vanwege de weak typing praktisch geen enkele ondersteuning biedt aan eigen datatypen zoals dat bij andere programmeertalen wel het geval is. Je kunt in PHP bijvoorbeeld geen enum definieren waarin je meerdere scalars of andere objecten combineert, zoals met structs in C. Vanwege dit gebrek zou je kunnen overgaan om voor elke coördinaat een apart object te instantiëren. Maar erg efficiënt is dat natuurlijk niet.
Met value objects word je als programmeur bij gebrek aan beter eigenlijk de totaal verkeerde kant op gestuurd.
Nou hoef je dat niet van mij aan te nemen, ik heb mijn bias. Maar ik ben niet de enige die dat zo ziet, er is sinds ruim anderhalf jaar een RFC voor structs. Het enige nadeel is dat je nog niet kunt weten hoe lang je er op moet gaan wachten, als het al in PHP geïmplementeerd gaat worden. (status van implementatie is "To Be Determined") ...