exception classes
Pagina: « vorige 1 2 3 volgende »
jupperdejuppie
Oké... dan klopt het dus hoe ik het nu doe :)
Toevoeging op 17/11/2013 15:47:56:
>> jupperdejuppie
Jeee!! :-)))
Toevoeging op 17/11/2013 17:05:30:
Wouter, nog even een vraagje over wat jij eerder zei. Jij zei dat het nuttig is om een exception hiërarchie op te bouwen.
>> 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...
Kun je een praktijk-voorbeeld geven wanneer zo'n hiërarchie een meerwaarde kan zijn?
Wat ik namelijk niet helemaal begrijp... stel we hebben een FooException, en we hebben 5 andere Exceptions die de FooException extenden. Wat is dan precies daar de meerwaarde van? Blijkbaar kunnen we dit doen om in 1x alle FooExceptions te catchen? Oké, prima... maar als dat de reden is, en we blijkbaar alle FooExceptions op dezelfde manier willen behandelen, waarom gebruiken we dan niet slechts 1 FooException?
In de praktijk... waarom zouden we bijv. een DirectoryException maken en een FileException, die beiden de FileSystemException extenden? Ik kan toch gewoon 1 FileSystemException gebruiken en via $message aangeven wat er fout gaat ("Er kan geen directory worden aangemaakt" of "De file kan niet worden opgeslagen".) Waarom zou je hier meerdere Exceptions voor willen gebruiken?
Gewijzigd op 17/11/2013 17:06:33 door Ozzie PHP
Ozzie PHP op 17/11/2013 15:47:34:
In de praktijk... waarom zouden we bijv. een DirectoryException maken en een FileException, die beiden de FileSystemException extenden? Ik kan toch gewoon 1 FileSystemException gebruiken en via $message aangeven wat er fout gaat ("Er kan geen directory worden aangemaakt" of "De file kan niet worden opgeslagen".) Waarom zou je hier meerdere Exceptions voor willen gebruiken?
Zou je daarvoor dan niet op zijn minst $code gebruiken met een unieke integer/constante? Als het toch één exception is, kan je daarmee in elk geval nog varianten van dezelfde exception onderscheiden.
Een file system is sowieso een klasse apart (hint-hint) omdat een directory technisch weinig meer is dan een bestand dat andere bestanden bevat. Hetzelfde geldt voor een zip-bestand of ander type archive. En hetzelfde geldt voor constructies zoals een station en een partitie. Je hebt hier een iterator nodig en daarmee een speciale exception.
Waarvoor zou je dan precies een code gebruiken? Ik kan toch ook een message gebruiken om te zeggen wat er mis is? Als ik bijv. zou zeggen, foutcode is 20, dan weet ik niet wat er wordt bedoeld. Als ik echter een message hebt die zegt "Ik kan geen directory aanmaken" dan snap ik wel wat er wordt bedoeld. Wat is de meerwaarde van een code?
Kan iemand dan een praktijk-voorbeeld geven wanneer het extenden van Exceptions nuttig is, want dat begrijp ik nog steeds niet.
Dos, thanks... maar wat heeft dit precies met mijn vraag te maken?? :)
Het tweede niet. (Ik reken \Exception even niet als hiërarchie.)
Een hele groep van exceptions af kunnen vangen is als programmeur fijner dan elke exception individueel afvangen.
Ik snap technisch gezien wel wat je doen, maar ik zie het nut tot op heden nog niet. Jouw voorbeeld zou je ook iedere exception de standaard exception kunnen laten extenden en dan in het catch-blok kunnen controleren op Exception.
Ik vind het nog een beetje vaag, en ik zie tot nu toe eerlijk gezegd ook nog geen enkel praktijk-voorbeeld.
Daarom zal die cacher gaan catchen op de algemene DatabaseException. Dat terwijl de database klasse alleen maar CouldNotConnectException of InvalidQueryException gooit.
Dan hebben we nu een UserMapper. Zodra we daar een user by id opvragen zal hij ook die database klasse willen gebruiken. Voor hem is het echter wel belangrijk wat het type exception is. De CouldNotConnectException zal hij namelijk lekker moeten laten opborrelen, maar een InvalidQueryException is een exception die aangeeft dat hij iets fout doet. Dat moet hij dus zelf zien op te lossen.
Daarom is het gebruik van hierarchy belangrijk.
En een code is inderdaad onduidelijk, behalve als je er een constante aan hangt. Bijv. CouldNotConnectException::ERR_INVALID_CREDENTIALS. Je gebruikt codes omdat je weet dat je deze verschillende typen exceptions niet apart wilt catchen, maar je wilt toch onderscheid ertussen maken.
Dat van die codes snap ik nog niet helemaal. Ten eerste, gebruik jij echt een code (een nummertje)? En ten tweede, wat is het verschil of ik dan die code meegeef, of in mijn $message "Invalid credentials" zet. Dat komt toch op hetzelfde neer?
Ja, message en code komt op het zelfde neer. De code is echter voor de computer goed begrijpbaar en de message voor de mens.
http://kohanaframework.org/3.3/guide-api/HTTP_Exception dan.
In Request_Client_Internal::execute_request() staat het volgende:
Op die manier kun je de get_response() van HTTP_401_Exception en HTTP_404_Exception overschrijven en de rest (op HTTP_Expected en subclasses na) houd het standaard gedrag.
Zonder een hiërarchie zou je heel veel code herhalen. Wat niet bepaald DRY is.
Kijk maar eens naar In Request_Client_Internal::execute_request() staat het volgende:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
try
{
...
}
catch (HTTP_Exception $e)
{
// Get the response via the Exception
$response = $e->get_response();
}
catch (Exception $e)
{
// Generate an appropriate Response object
$response = Kohana_Exception::_handler($e);
}
?>
try
{
...
}
catch (HTTP_Exception $e)
{
// Get the response via the Exception
$response = $e->get_response();
}
catch (Exception $e)
{
// Generate an appropriate Response object
$response = Kohana_Exception::_handler($e);
}
?>
Op die manier kun je de get_response() van HTTP_401_Exception en HTTP_404_Exception overschrijven en de rest (op HTTP_Expected en subclasses na) houd het standaard gedrag.
Zonder een hiërarchie zou je heel veel code herhalen. Wat niet bepaald DRY is.
Oke, duidelijk. Maar wat doe je dan met die code? Waarvoor gebruik je die?
Als je bijv. een filesystem class hebt, wat voor soorten exceptions moet die dan kunnen gooien? Ik dacht in 1e instantie dat deze class gewoon telkens als er iets fout gaat een FilesystemException zou moeten gooien. Eerder werd ergens gesteld dat iedere class z'n eigen exception moet hebben, dus dit leek me de juiste oplossing. Nu loop ik tegen het probleem aan dat ik in de problemen kom.
Stel ik doe in class Foo dit:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
try {
$this->filesystem->deleteDirectory($dir);
} catch (FilesystemException $e) {
// directory kan niet worden verwijderd
}
?>
try {
$this->filesystem->deleteDirectory($dir);
} catch (FilesystemException $e) {
// directory kan niet worden verwijderd
}
?>
En in de Filesystem class staat zoiets:
Code (php)
Zoals je ziet kunnen er in deze method 2 exceptions optreden. Op het moment dat de directory al bestaat, en op het moment dat de directory niet kan worden verwijderd.
We kunnen nu geen onderscheid maken tussen de beide exceptions. Oplossing: we gaan 2 verschillende exceptions gooien en vangen:
Class Foo:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
try {
$this->filesystem->deleteDirectory($dir);
} catch (RunTimeException $e) {
// directory kan niet worden verwijderd
}
?>
try {
$this->filesystem->deleteDirectory($dir);
} catch (RunTimeException $e) {
// directory kan niet worden verwijderd
}
?>
En de Filesystem class:
Code (php)
Nu gaat het wel goed, maar gebruik ik ineens dus helemaal geen FilesystemException meer. Dat lijkt me ook niet kloppen toch? Ik begreep eerder dat het goed is dat een class zijn eigen exception heeft, maar als ik in dit voorbeeld een FilesystemException zou gebruiken, dan werkt het niet. Dus ik snap nu even niet A) of je wel of niet eigen exceptions moet gebruiken en B) zo ja, wanneer en op welke manier. Ik zit aardig op het goede spoor, maar iets klopt er nog niet helemaal in mijn gedachten. Kan iemand me weer wat wijzer maken?
Gewijzigd op 01/12/2013 03:33:38 door Ozzie PHP
Ozzie PHP op 01/12/2013 03:29:42:
Je maakt een logicafout. Je kunt alleen een directory verwijderen die bestaat. Je zou hier dus !is_dir($dir) voor "is geen directory" verwachten. Daarmee krijg je één FilesystemException voor twee samenhangende fouten:
Als je het even test, blijkt rmdir() zelf al te controleren of de directory bestaat. Bestaat de directory namelijk niet, dan krijg je een warning: "No such file or directory." Aangezien een is_dir() al is geïmplementeerd in rmdir(), kun je het vereenvoudigen tot:
Code (php)
Je maakt een logicafout. Je kunt alleen een directory verwijderen die bestaat. Je zou hier dus !is_dir($dir) voor "is geen directory" verwachten. Daarmee krijg je één FilesystemException voor twee samenhangende fouten:
Code (php)
Als je het even test, blijkt rmdir() zelf al te controleren of de directory bestaat. Bestaat de directory namelijk niet, dan krijg je een warning: "No such file or directory." Aangezien een is_dir() al is geïmplementeerd in rmdir(), kun je het vereenvoudigen tot:
Gewijzigd op 01/12/2013 09:31:32 door Ward van der Put
Als je een directory verwijdert die niet bestaat en je gooit vervolgens een FilesystemException, dan zal class Foo dus denken dat het verwijderen is mislukt, en dat de directory nog steeds bestaat. Class Foo zal dus denken dat het fout gaat.
Een beter en duidelijker voorbeeld is het aanmaken van een directory. Zelfde principe... Class Foo maakt een directory aan en wil daarna een aantal bestanden in die directory plaatsen. Jouw code (aangepast):
Code (php)
En in class Foo:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
try {
$this->filesystem->createDirectory($dir);
} catch (FilesystemException $e) {
// directory kan niet worden aangemaakt
}
?>
try {
$this->filesystem->createDirectory($dir);
} catch (FilesystemException $e) {
// directory kan niet worden aangemaakt
}
?>
Stel nu dat de directory al bestaat, dan geeft jouw code een FilesystemException, waardoor class Foo zal denken dat de directory niet kon worden aangemaakt. Class Foo gaat er dus vanuit dat de directory niet bestaat en zal er dus ook geen bestanden in gaan plaatsen. Het gaat dan dus mis, terwijl de bestanden eigenlijk wel hadden kunnen worden geplaatst, omdat de directory al bestond.
Het gooien van slechts 1 type exception is dus volgens mij niet toereikend???
Gewijzigd op 01/12/2013 10:55:23 door Ozzie PHP
Ozzie PHP op 01/12/2013 10:52:19:
Stel nu dat de directory al bestaat, dan geeft jouw code een FilesystemException, waardoor class Foo zal denken dat de directory niet kon worden aangemaakt. Class Foo gaat er dus vanuit dat de directory niet bestaat en zal er dus ook geen bestanden in gaan plaatsen. Het gaat dan dus mis, terwijl de bestanden eigenlijk wel hadden kunnen worden geplaatst, omdat de directory al bestond.
De klasse Foo denkt zelf niets, maar belandt in de catch. Daarin plaats je vervolgens een oplossing.
Je voorbeeld bevat al een duidelijke ontwerpbeslissing: het maken van een directory mag mislukken als de directory al bestaat, want dan kunnen we daarin bestanden opslaan. Dan kun je dat formaliseren in de naam en code van de methode. Even in een complete if-elseif-else om alle cases af te dekken:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
/**
* @param string $dir Path to the directory to check for and create.
* @return bool
* @throws FilesystemException
*/
public function createDirectoryIfNotExists($dir)
{
if (is_dir($dir)) {
return true;
} elseif (!mkdir($dir)) {
throw new FilesystemException('Could not create directory.');
} else {
return true;
}
}
?>
/**
* @param string $dir Path to the directory to check for and create.
* @return bool
* @throws FilesystemException
*/
public function createDirectoryIfNotExists($dir)
{
if (is_dir($dir)) {
return true;
} elseif (!mkdir($dir)) {
throw new FilesystemException('Could not create directory.');
} else {
return true;
}
}
?>
Gaat dat je te ver, dan moet je de try of de catch van Foo uitbreiden. En dat moet eigenlijk ook als je ontwerpbeslissing vooral voor Foo geldt.
Het valt me trouwens op dat je true returnt. Is dat iets wat je altijd doet? Ik bedoel, waarom return je een boolean als je toch al een exception gooit? Dat lijkt me dubbelop?
Als jij het okay vindt dat een directory niet wordt gemaakt als die al bestaat, dan is dat geen exception waard. Het systeem gedraagt zich dan namelijk zoals jij het ontworpen hebt.
Waarom return je per se iets? Je kunt toch ook helemaal niks returnen, maar alleen een exception gooien? Als er dan geen exception wordt opgevangen weet je dat het goed is gegaan :) Het heeft (wat mij betreft) alleen zin om iets te returnen als je ook iets met die waarde gaat doen, maar dat is nu niet het geval.
Filesystem::directoryExists() ... haha, ja daar heb je dus een punt waar ik lang over heb moeten denken. Maar dan zou ik dus altijd voordat ik een directory aanmaak, moeten controleren of die wel of niet bestaat. Ik weet niet of dat handig is? Doe jij dat ook op die manier?