Selbstmord bei Objekten

Selbstmord bei Objekten

am 06.04.2008 23:47:19 von Thomas Mlynarczyk

Hallo,

Mal angenommen, ich instantiiere ein Objekt:

$db = new DatenbankServer( $verbindungsdaten );

Im Konstruktor wird die Verbindung zur Datenbank hergestellt und dabei
geht irgendwas schief (d.h. die Verbindung kann nicht hergestellt
werden). Dann habe ich mein Objekt $db, aber es ist völlig unbrauchbar
und daher sollten alle Methodenaufrufe FALSE zurückliefern (oder ähnlich
reagieren). Nun kann ich natürlich in diesem Fall den Konstruktor eine
private Eigenschaft $this->_bIchBinTot = true setzen lassen, die dann
allerdings von jeder Methode abgefragt werden müßte, was mir ein bißchen
umständlich erscheint. Die einzigen Alternativen, die mir einfallen, wären:

1) den Konstruktor eine Exception schmeißen zu lassen
2) das Script einfach per trigger_error()/exit zu beenden
3) settype( $this, 'null' )

Bei 1) hätte ich nach wie vor noch mein $db-Objekt, 2) erscheint mir
unnötig brutal und 3) funktioniert nicht. Gibt es für solche Fälle ein
Patentrezept?

Gruß,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Re: Selbstmord bei Objekten

am 06.04.2008 23:57:02 von Niels Braczek

Thomas Mlynarczyk schrieb:

> 1) den Konstruktor eine Exception schmeißen zu lassen
> 2) das Script einfach per trigger_error()/exit zu beenden
> 3) settype( $this, 'null' )
>=20
> Bei 1) hätte ich nach wie vor noch mein $db-Objekt,

Warum sollte es? Aufgrund der Exception wird doch $db gar nicht gesetzt.

> 2) erscheint mir=20
> unnötig brutal und 3) funktioniert nicht. Gibt es für solche Fäll=
e ein=20
> Patentrezept?

try {
$db =3D new DatenbankServer( $verbindungsdaten );
} catch (Exception $e) {
echo "Nix DB heute";
}

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Selbstmord bei Objekten

am 07.04.2008 00:43:39 von Ulf Kadner

Niels Braczek wrote:
> try {
> $db = new DatenbankServer( $verbindungsdaten );
> } catch (Exception $e) {
> echo "Nix DB heute";
> }

Eine Anmerkung:

Es gibt viele Situationen in denen Ausnahmen im Konstruktor passend
sind, aber auch genau so viele in denen das ungewollt ist.

Sobald es hier zu einer Logik kommt die erkennbar zu Fehlern führen kann
und Ausnahmen unpassend erscheinen (Nutzerinteraktion z.B.) lagere ich
das so aus das di Fehler-Logic vor dem Konstruktor aufgeführt wird und
das Objekt direkt für den Caller erkennbar initialisiert wird.

class A
{
public function __construct() { ... }
public static function Create()
{
if (???)
{
self::registerError('...');
return false;
}
return new A();
}
}

Da kann man recht easy mit:

if (FALSE === ($instance = A::Create()))
# mache irgendwas bei Fehler

MfG, Ulf

Re: Selbstmord bei Objekten

am 07.04.2008 04:57:00 von Niels Braczek

Ulf Kadner schrieb:
> Niels Braczek wrote:

>> try {
>> $db =3D new DatenbankServer( $verbindungsdaten );
>> } catch (Exception $e) {
>> echo "Nix DB heute";
>> }
>=20
> Eine Anmerkung:
>=20
> Es gibt viele Situationen in denen Ausnahmen im Konstruktor passend=20
> sind, aber auch genau so viele in denen das ungewollt ist.
> ...
> if (FALSE ===3D ($instance =3D A::Create()))
> # mache irgendwas bei Fehler

Was ist hier jetzt anders als im catch()-Block? Dein Beispiel sieht mir
nach einem Überbleibsel aus der prozeduralen Vorgehensweise aus.

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Selbstmord bei Objekten

am 07.04.2008 11:06:19 von Ulf Kadner

Niels Braczek wrote:

> Was ist hier jetzt anders als im catch()-Block? Dein Beispiel sieht mir
> nach einem Überbleibsel aus der prozeduralen Vorgehensweise aus.

Vergiss mein Posting ganz schnell wieder. Keine Ahnung was ich sagen
wollte. War wohl etwas zu spät für klare Gedanken bei mir.

:-(

MfG, Ulf

Re: Selbstmord bei Objekten

am 07.04.2008 13:59:01 von Thomas Mlynarczyk

Niels Braczek schrieb:

>> 1) den Konstruktor eine Exception schmeißen zu lassen
>> Bei 1) hätte ich nach wie vor noch mein $db-Objekt,

> Warum sollte es? Aufgrund der Exception wird doch $db gar nicht gesetzt.

Ja, stimmt ja eigentlich, wenn ich in etwas ausgeschlafenerem Zustand
nochmal darüber nachdenke. Die Zuweisung $db = ... wird ja gar nicht
ausgeführt, wenn mir die rechte Seite "wegspringt". $db wäre dann
"undefined" - oder doch schon mit dem Wert NULL angelegt?

> try {
> $db = new DatenbankServer( $verbindungsdaten );
> } catch (Exception $e) {
> echo "Nix DB heute";
> }

Also Du würdest sagen, daß eine Exception hier die beste Lösung ist? Ich
habe mal irgendwo gelesen "Exceptions are exceptions" und überlege
deshalb jedesmal doppelt und dreifach, ob ich eine verwenden soll. Die
von Ulf vorgeschlagene Lösung hätte ja den Vorteil, daß der Programmfluß
nicht unterbrochen wird (immer alles schön der Reihe nach, ohne
Sprünge). Allerdings finde ich $instance = A::Create() ein bißchen
unelegant. Bei Exceptions hingegen darf man nicht vergessen, sie in
try/catch einzupacken, sonst ist die Wirkung die gleiche wie error+exit.
Aber je länger ich darüber nachdenke, umso sinnvoller erscheinen sie mir.

Gruß + Dank,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Re: Selbstmord bei Objekten

am 07.04.2008 14:34:15 von Wolfgang Fellger

Thomas Mlynarczyk schrieb:

>Also Du würdest sagen, daß eine Exception hier die beste Lösung ist? Ich
>habe mal irgendwo gelesen "Exceptions are exceptions" und überlege deshalb
>jedesmal doppelt und dreifach, ob ich eine verwenden soll.

Da hatten wir ja erst derletzt eine Diskussion drüber. :-)
Was man als "normaler Programmfluss" betrachtet ist natürlich dehnbar.
Solange du Exceptions zur Fehlerbehandlung und nicht als Goto-Ersatz
verwendest ist der Einsatz mit Sicherheit vertretbar - zu irgendwas ist das
Sprachfeature ja wohl da.

Ich sehe 2) und 3) nicht als Alternative zum Werfen einer Exception - 2 ist
unnötig brutal, zumal man wenn notwendig das selbe Verhalten auch mit
Exceptions bekommen kann, 3 ist ein Hack (wenn er funktioniert).

Eine wirkliche Alternative ist schon eher das was du als "umständlich"
beschreibst - das Objekt wird zwar erzeugt, allerdings in einem speziellen
Zustand, in dem man nicht viel damit anfangen kann.

Wenn dein Objekt also mit der Fehlersituation NICHT umgehen können soll, weil
das den Code einfach hält, solltest du beim Erzeugen eine Exception werfen
und es so gar nicht erst entstehen lassen. Das ist legitim, weil du
/entschieden/ hast dass das Objekt mit der Fehlersituation nicht umgehen
kann. Natürlich gibst du damit die volle Verantwortung für die
Fehlerbehandlung an den aufrufenden Code weiter. Häufig wird man das sowieso
wollen; konkret musst du natürlich von Fall zu Fall entscheiden.

--
Wolfgang Fellger

Re: Selbstmord bei Objekten

am 07.04.2008 15:46:55 von Niels Braczek

Thomas Mlynarczyk schrieb:
> Niels Braczek schrieb:
>=20
>>> 1) den Konstruktor eine Exception schmeißen zu lassen
>>> Bei 1) hätte ich nach wie vor noch mein $db-Objekt,
>=20
>> Warum sollte es? Aufgrund der Exception wird doch $db gar nicht gesetz=
t.
>=20
> Ja, stimmt ja eigentlich, wenn ich in etwas ausgeschlafenerem Zustand=20
> nochmal darüber nachdenke. Die Zuweisung $db =3D ... wird ja gar nich=
t=20
> ausgeführt, wenn mir die rechte Seite "wegspringt". $db wäre dann=20
> "undefined" - oder doch schon mit dem Wert NULL angelegt?

Wenn du willst, dass $db im Fehlerfalle mit null belegt ist, stelle das
sicher:

$db =3D null;
>> try {
>> $db =3D new DatenbankServer( $verbindungsdaten );
>> } catch (Exception $e) {
// $db ist null
>> echo "Nix DB heute";
>> }

> Also Du würdest sagen, daß eine Exception hier die beste Lösung i=
st?

Ja.

> Ich=20
> habe mal irgendwo gelesen "Exceptions are exceptions" und überlege=20
> deshalb jedesmal doppelt und dreifach, ob ich eine verwenden soll. Die =

> von Ulf vorgeschlagene Lösung hätte ja den Vorteil, daß der Progr=
ammfluß
> nicht unterbrochen wird (immer alles schön der Reihe nach, ohne=20
> Sprünge).

Das sieht nur so aus. Ulfs Vorschlag ist letztlich genau dasselbe in
grün (von Patina). if/else oder try/catch ist erstmal gleichwertig. Der=

Vorteil von Exceptions und try/catch zeigt sich, sobald mehrere möglich=
e
Fehlerquellen existieren:

try {
$Db =3D new DatabaseServer( $connectionData );
$Query =3D $Db->createQuery( $someSQL );
$Records =3D $Query->execute();
foreach ( $Records as $Record ) {
$Record->doAnything( $withSomething );
}
} catch (Exception $e) {
// beliebig detaillierte Fehlerbehandlung
// die in der IDE zudem weg-gefaltet werden kann.
}

statt

$Db =3D new DatabaseServer( $connectionData );
if ( is_object( $Db ) ) {
$Query =3D $Db->createQuery( $someSQL );
if ( is_object ( $Query ) ) {
$Records =3D $Query->execute();
if ( is_object( $Records ) ) { // Records sei einfach
foreach ( $Records as $Record ) { // mal transversable
if ( !$Record->doAnything( $withSomething ) ) {
// Fehlerbehandlung Record
break;
}
}
} else {
// Fehlerbehandlung Records
}
} else {
// Fehlerbehandlung Query
}
} else {
// Fehlerbehandlung Db
}

Ersteres finde ich *erheblich* übersichtlicher.

> Allerdings finde ich $instance =3D A::Create() ein bißchen=20
> unelegant. Bei Exceptions hingegen darf man nicht vergessen, sie in=20
> try/catch einzupacken, sonst ist die Wirkung die gleiche wie error+exit=
.

Genau, und zwar an der Stelle, an der das Skript entscheiden kann, wie
mit der Situation umgegengen werden soll.

> Aber je länger ich darüber nachdenke, umso sinnvoller erscheinen si=
e mir.

Es dauert erfahrungsgemäß ein wenig, bis man sich daran gewöhnt hat=
Ich
bin selber auch noch nicht ganz durch damit.

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Selbstmord bei Objekten

am 07.04.2008 20:25:52 von Thomas Mlynarczyk

Wolfgang Fellger schrieb:

> Wenn dein Objekt also mit der Fehlersituation NICHT umgehen können soll, weil
> das den Code einfach hält, solltest du beim Erzeugen eine Exception werfen
> und es so gar nicht erst entstehen lassen. Das ist legitim, weil du
> /entschieden/ hast dass das Objekt mit der Fehlersituation nicht umgehen
> kann. Natürlich gibst du damit die volle Verantwortung für die
> Fehlerbehandlung an den aufrufenden Code weiter. Häufig wird man das sowieso
> wollen; konkret musst du natürlich von Fall zu Fall entscheiden.

Ja. Eigentlich kann ja - in meinem Beispiel - nur dann ein Fehler
auftreten, wenn die DB-Verbindung nicht zustande gekommen ist. Und das
passiert entweder, wenn die falschen Verbindungsdaten angegeben wurden
(Programmierer selber schuld und das hat man zu merken, bevor das Ding
produktiv eingesetzt wird), oder der DB-Server abgeschmiert ist - in
letzterem Fall dürfte man andere Probleme haben, als eine nicht
abgefangene Exception.

Gruß,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Re: Selbstmord bei Objekten

am 07.04.2008 20:32:05 von Thomas Mlynarczyk

Niels Braczek schrieb:

> Wenn du willst, dass $db im Fehlerfalle mit null belegt ist, stelle das
> sicher:

> $db = null;
>>> try {
>>> $db = new DatenbankServer( $verbindungsdaten );
>>> } catch (Exception $e) {
> // $db ist null
>>> echo "Nix DB heute";
>>> }

Das könnte ich doch dann auch direkt im catch-Block machen, oder? Wäre
eine Anweisung weniger, die im Erfolgsfall ausgeführt werden muß.

>> Also Du würdest sagen, daß eine Exception hier die beste Lösung ist?

> Ja.

Damit wäre mein Problem gelöst, danke.

> try {
> $Db = new DatabaseServer( $connectionData );
> $Query = $Db->createQuery( $someSQL );
> $Records = $Query->execute();
> foreach ( $Records as $Record ) {
> $Record->doAnything( $withSomething );
> }
> } catch (Exception $e) {
> // beliebig detaillierte Fehlerbehandlung
> // die in der IDE zudem weg-gefaltet werden kann.
> }

Und ich kann ja auch verschiedene Exceptions schmeißen lassen und mit
mehreren catch-Blöcken einzeln behandeln.

Gruß + Dank,
Thomas

--
Ce n'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!
(Coluche)

Re: Selbstmord bei Objekten

am 08.04.2008 00:49:12 von Niels Braczek

Thomas Mlynarczyk schrieb:
> Niels Braczek schrieb:
>=20
>> Wenn du willst, dass $db im Fehlerfalle mit null belegt ist, stelle da=
s
>> sicher:

> Das könnte ich doch dann auch direkt im catch-Block machen, oder? Wä=
re=20
> eine Anweisung weniger, die im Erfolgsfall ausgeführt werden muß.

Ja, kannst du machen. Ich belege die Variablen prinzipiell mit ihrem
Default-Wert vor, falls sie einen haben. In diesem Fall reine Stilfrage.

> Damit wäre mein Problem gelöst, danke.
> Und ich kann ja auch verschiedene Exceptions schmeißen lassen und mit=
=20
> mehreren catch-Blöcken einzeln behandeln.

Exact.

MfG
Niels

--=20
| http://www.kolleg.de =B7 Das Portal der Kollegs in Deutschland |
| http://www.bsds.de =B7 BSDS Braczek Software- und DatenSysteme |
| Webdesign =B7 Webhosting =B7 e-Commerce =B7 Joomla! Content Management =
|
------------------------------------------------------------ ------

Re: Selbstmord bei Objekten

am 10.04.2008 15:44:02 von Christoph Herrmann

Thomas Mlynarczyk schrieb:
> Also Du würdest sagen, daß eine Exception hier die beste Lösung ist? Ich
> habe mal irgendwo gelesen "Exceptions are exceptions" und überlege

Ist es denn nicht ein Ausnahmezustand, wenn keine Verbindung zur
Datenbank hergestellt werden kann? :)

--
Mit freundlichen Grüßen,
Christoph Herrmann

http://dragonprojects.de/

Re: Selbstmord bei Objekten

am 11.04.2008 08:06:41 von foo

Christoph Herrmann schrieb:

>> Also Du würdest sagen, daß eine Exception hier die beste Lösung ist?
>> Ich habe mal irgendwo gelesen "Exceptions are exceptions" und überlege
>
> Ist es denn nicht ein Ausnahmezustand, wenn keine Verbindung zur
> Datenbank hergestellt werden kann? :)

Per Definition nicht.
Aber das kann deine Konzeption natürlich anders sehen.

Gruß,
Torsten