Door
Ozzie PHP
op 16-11-2013 01:35
gewijzigd op 16-11-2013 01:37
5.003 views
Ola peepz,
Nog even een vraagje over eigen exception classes.
Mij werd dus aangeraden om eigen exception classes te gebruiken. Nu zien die classes er allemaal ongeveer zo uit:
<?php
namespace Foo;
use Bar\MyException;
class Exception extends MyException {
public function __construct($message, $code, $previous = null) {
parent::__construct($message, $code, $previous);
}
}
?>
Nu is het zo dat de MyException class een constructor heeft die identiek is aan de constructor van de Foo Exception class. De constructor in de Foo Exception class levert op dit moment dus geen meerwaarde en zou ik dus compleet achterwege kunnen laten. (Hierbij ga ik er nu even vanuit dat ik de $message niet wil aanpassen met een of andere standaardtekst.)
Als ik de constructor zou weglaten, krijg ik als het ware een lege class. Is dat niet vreemd? Of is dit gewoon oké?
>> dan zou als ik ergens een DatabaseException opvang, de afhandeling voor een niet werkende connectie (situatie 1) en het niet verkrijgen van data (situatie 2) hetzelfde worden afgehandeld. Situatie 2 kan echter in de praktijk gewoon voorkomen zonder dat dit een ernstige fout hoeft te zijn. Situatie 1 is echter een ander geval. Als ik geen connectie met de database kan maken moeten alle alarmbellen afgaan en moet ik actie ondernemen.
En dat is juist het mooie van het gebruik van hierarchy. In sommige gevallen wil je gewoon alle database problemen, of zelfs alle problemen, op vangen, terwijl je in andere gevallen alleen de connectie problemen wil opvangen. Het gebruik van hierarchy is juist het mooie van exceptions, gooi je dat weg kan je lekker alleen met een base klasse gaan werken...
En voor die laatste alinea, lees eens mijn reactie. Overal een exception klasse voor maken is onzin (vooral als je geen hierarcy gebruikt,..)
Ik gaf bewust voorbeelden van de twee uitersten: elke fout afhandelen met een eigen exception enerzijds of alles met dezelfde exception-klasse afhandelen. Dan is namelijk duidelijk waarover we het wel eens zijn: tussen die twee uitersten zul je een middenweg moeten vinden :-)
Een class Foo voorzien van een FooException, een class Bar van een BarException, enzovoort kán inderdaad een oplossing zijn voor klasse-specifieke fouten. Maar er zijn ook universele fouten die niet klasse-gebonden zijn. Neem bijvoorbeeld een fout van het type "ongeldig datatype" of een fout uit de categorie "waarde te groot/klein".
Een voorbeeld... iDEAL vereist een bedrag in centen (een integer) tussen 84 en 1000000. Toch vind ik het opgeven van iets anders dan een integer geen iDEAL-specifieke iDEALException waard. Het is een veel universelere fout: deze methode verwacht een integer, maar er kwam iets anders binnen.
<?php
/**
* @api
* @param int $amount An iDEAL amount in cents.
* @return this
* @throws \InvalidArgumentException if the amount is not an integer.
* @throws \RangeException if the amount is too small or too large.
*/
public function setAmount($amount)
{
$amount = filter_var($amount, FILTER_VALIDATE_INT);
if ($amount === false) {
throw new \InvalidArgumentException('The iDEAL amount is not an integer.');
} elseif ($amount < 84) {
throw new \RangeException('The iDEAL amount is too small.');
} elseif ($amount > 1000000) {
throw new \RangeException('The iDEAL amount is too large.');
} else {
$this->Amount = $amount;
}
return $this;
}
?>
class A probeert iets te cachen en gebruikt hiervoor de cacher (class C). Deze cacher gebruikt op haar beurt weer een FileSystem class (class F) om het bestand op te slaan. Dus:
class A -> class C -> class F
Als een bestand niet kan worden weggeschreven gooit class F een FileSystem exception. Vervolgens gooit class C een Cacher exception. Class A controleert vervolgens in het catch-blok of er een Cacher exception is gegooid. Kan ik nu dan ook net zo goed in plaats van een FileSystem exception en een Cacher exception gewoon 2x een algemene exception gooien? En in class A controleren of er een algemene Exception is gegooid?
Ward, in dat geval zal ik alsnog een Ideal\RangeException en Ideal\InvalidArgumentException maken.
Betaalmethoden extenden hetzelfde request-object voor de API van een betaalprovider en hebben allemaal een setAmount()-methode. Ik heb daarmee zoiets:
<?php
namespace TargetPay;
use TargetPay\Request as Request;
final class iDEAL extends Request
{
public method setAmount($amount)
{
// throw new ... ?
}
}
?>
Waar zou jij de grens voor wel/geen unieke exception dan leggen? Bij de namespace? De Request-ouderklasse? Of toch de iDEAL-klasse? Of misschien alles een kwartslag draaien en een InvalidAmountException opvoeren voor alle betaalsystemen?
Ja, ik wil hier een CacherException zien. Dat de Cacher het FileSystem gebruikt, is niet relevant. Een verbeterde Cacher 2.0.0 kan een andere store gebruiken: het RAM-geheugen, een database, de cloud. Maar daarvan mogen "derden" die de Cacher inzetten geen last hebben. Ze hoeven het niet eens te weten.
Overigens kun je uit een trace altijd nog afleiden waar die CacherException vandaan komt als je gaat debuggen.
>> Waar zou jij de grens voor wel/geen unieke exception dan leggen? Bij de namespace? De Request-ouderklasse? Of toch de iDEAL-klasse? Of misschien alles een kwartslag draaien en een InvalidAmountException opvoeren voor alle betaalsystemen?
Ik zou inderdaad niet per klasse is specifieks doen, behalve als het specifiek is voor die betaalmethode. Globaal kun je je code altijd indelen in verschillende componenten: Router, Validation, Form, etc. Deze component hebben allemaal hun eigen exceptions.
Ik zou dus een TargetPay\Exception\InvalidAmountException maken. Over het algemeen moeten alle children van een klasse dezelfde exceptions gebruiken, aangezien je ongemerkt de klasse moet kunnen veranderen.
Toevoeging op 17/11/2013 15:40:07:
>> Vervolgens gooit class C een Cacher exception. Class A controleert vervolgens in het catch-blok of er een Cacher exception is gegooid. Kan ik nu dan ook net zo goed in plaats van een FileSystem exception en een Cacher exception gewoon 2x een algemene exception gooien? En in class A controleren of er een algemene Exception is gegooid?
Nee, geen algemene exceptions. De Class A zal echter alleen een Cacher exception willen ontvangen (met als previous exception de FileSystemException). Het zal me een worst wezen wat die cacher heeft gedaan waardoor het niet lukte, dat heb ik alleen nodig bij het debuggen. Voor de code wil ik alleen weten dat die Cacher het niet is gelukt.
>> Ja, ik wil hier een CacherException zien. Dat de Cacher het FileSystem gebruikt, is niet relevant. Een verbeterde Cacher 2.0.0 kan een andere store gebruiken: het RAM-geheugen, een database, de cloud. Maar daarvan mogen "derden" die de Cacher inzetten geen last hebben. Ze hoeven het niet eens te weten.
Dus mijn opzet zoals ik die nu heb die klopt dan gewoon?