Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Interfaces und Inheritance

17 views
Skip to first unread message

Stefan Froehlich

unread,
Dec 15, 2010, 5:51:20 PM12/15/10
to
Gerade eben darueber gestolpert:

| <?php
|
| interface A {};
| interface B extends A {};
|
| class C implements A, B {};
| class D implements B, A {};
|
| ?>

*grr*

Soll das ein Bug oder ein Feature sein? Ich meine, es laesst sich
erahnen, woher das Problem stammt, und man kann auch recht einfach
die Reihenfolge entsprechend anpassen - aber als wirklich sinnvoll
erscheint mir das dennoch nicht unbedingt...

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Ungewöhnliches für gewöhnliche Leute: Stefan - nicht nur für fröhliche Stunden!
(Sloganizer)

Niels Braczek

unread,
Dec 15, 2010, 10:10:08 PM12/15/10
to
Stefan Froehlich schrieb:

> Gerade eben darueber gestolpert:
>
> | <?php
> |
> | interface A {};
> | interface B extends A {};
> |
> | class C implements A, B {};
> | class D implements B, A {};
> |
> | ?>
>
> *grr*

Was passiert?

> Soll das ein Bug oder ein Feature sein? Ich meine, es laesst sich
> erahnen, woher das Problem stammt, und man kann auch recht einfach
> die Reihenfolge entsprechend anpassen - aber als wirklich sinnvoll
> erscheint mir das dennoch nicht unbedingt...

Warum willst du überhaupt A angeben? Das ist doch in B enthalten...

MfG
Niels

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

Stefan Froehlich

unread,
Dec 16, 2010, 2:58:51 AM12/16/10
to
On Thu, 16 Dec 2010 04:10:08 Niels Braczek wrote:
> > | interface A {};
> > | interface B extends A {};
> > |
> > | class C implements A, B {};
> > | class D implements B, A {};

> > *grr*

> Was passiert?

Eine Fehlermeldung bei der zweiten Klassendefinition.

> > Ich meine, es laesst sich erahnen, woher das Problem stammt, [...]

"implements A" funktioniert, "implements B" kennt diese Tatsache bereits
und baut darauf auf.

vs.

"implements B" funktioniert und holt dabei gleich das "implements A" nach.
Das folgende "implements B" fliegt auf die Schnauze.

> Warum willst du überhaupt A angeben? Das ist doch in B enthalten...

Bei so einem abstrakten Beispiel ist das natuerlich absurd. Aber ich habe
Klassen mit ein, zwei Dutzend Interfaces, die teilweise sehr praxisnahe
Aufgaben erbringen; ein Klassiker davon ist "sortierbar", der von vielen
anderen Schnittstellen benoetigt wird.

Das Verstaendnis einer Klasse steigt (in meinen Augen) deutlich, wenn man -
so man wissen moechte, ob ihre Elemente sortierbar sind - sich nicht erst
ueberlegen muss, ob das durch eine der zahlreichen anderen Schnittstellen
impliziert wird, sondern diese Tatsache auch explizit anfuehren kann.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan - der beklopptste Witz, den es je gab.
(Sloganizer)

Niels Braczek

unread,
Dec 16, 2010, 6:07:50 AM12/16/10
to
Stefan Froehlich schrieb:

> On Thu, 16 Dec 2010 04:10:08 Niels Braczek wrote:

> Das Verstaendnis einer Klasse steigt (in meinen Augen) deutlich, wenn man -
> so man wissen moechte, ob ihre Elemente sortierbar sind - sich nicht erst
> ueberlegen muss, ob das durch eine der zahlreichen anderen Schnittstellen
> impliziert wird, sondern diese Tatsache auch explizit anfuehren kann.

In dem Fall ist es eher sinnvoll, die Interfaces sauber getrennt zu
halten; mir ist zudem noch nie ein Fall untergekommen, bei dem es
sinnvoll gewesen wäre, Intefaces zu vererben. Und selbst wenn: Bei

class A {}
class B extends A {}
class C extends B {}

sieht man C auch nicht ohne Weiteres die Abstammung von A an. Daher sehe
ich die korrekte Deklaration für dein Beispiel in

interface A {}
interface B extends A {}

class C implements B {}

, da das konsistent ist.

Claus Reibenstein

unread,
Dec 16, 2010, 6:41:59 AM12/16/10
to
Stefan Froehlich schrieb:

> On Thu, 16 Dec 2010 04:10:08 Niels Braczek wrote:
>
>> Was passiert?
>
> Eine Fehlermeldung bei der zweiten Klassendefinition.

Ich _liebe_ diese ausführlichen, vielsagenden, alles erklärenden
Antworten ... :-(

Herrgottsackelzement, warum postest Du die Fehlermeldung nicht? Ist es
denn wirklich so schwer, mal von sich aus eine _vollständige_
Problembeschreibung abzugeben? Warum muss man immer erst 1000 mal
nachfragen, bis man alle Informationen hat, die man braucht, um
herauszufinden, was der Poster eigentlich möchte, damit man überhaupt
erst mal in die Lage versetzt wird, helfen zu können?

Oder willst Du vielleicht keine Hilfe? Kannst Du gerne haben ...

Gnaaa...!!!

Gruß. Claus

Torsten Zühlsdorff

unread,
Dec 16, 2010, 8:59:46 AM12/16/10
to
Claus Reibenstein schrieb:

>>> Was passiert?
>> Eine Fehlermeldung bei der zweiten Klassendefinition.
>
> Ich _liebe_ diese ausführlichen, vielsagenden, alles erklärenden
> Antworten ... :-(
>
> Herrgottsackelzement, warum postest Du die Fehlermeldung nicht?

Manchmal kann man sich auch anstellen:
$ php -r "interface A {};


> interface B extends A {};
>
> class C implements A, B {};
> class D implements B, A {};"

PHP Fatal error: Class D cannot implement previously implemented
interface A in Command line code on line 5
PHP Stack trace:
PHP 1. {main}() Command line code:0

Fatal error: Class D cannot implement previously implemented interface A
in Command line code on line 5

Call Stack:
0.0002 72232 1. {main}() Command line code:0


> Ist es
> denn wirklich so schwer, mal von sich aus eine _vollständige_
> Problembeschreibung abzugeben?

Ja. Und das ist eigentlich ein sehr interessanter Punkt.

Könnte irgendjemand den ersten Teil reposten - Claus filtert mich, aber
vielleicht hilft es seinem Verständnis.

Gruß,
Torsten
--
http://www.dddbl.de - ein Datenbank-Layer, der die Arbeit mit 8
verschiedenen Datenbanksystemen abstrahiert,
Queries von Applikationen trennt und automatisch die Query-Ergebnisse
auswerten kann.

Benjamin Zikarsky

unread,
Dec 16, 2010, 9:14:46 AM12/16/10
to
Am 16.12.2010 14:59, schrieb Torsten Zühlsdorff:

> $ php -r "interface A {};
>> interface B extends A {};
>>
>> class C implements A, B {};
>> class D implements B, A {};"
> PHP Fatal error: Class D cannot implement previously implemented
> interface A in Command line code on line 5
> PHP Stack trace:
> PHP 1. {main}() Command line code:0
>
> Fatal error: Class D cannot implement previously implemented interface A
> in Command line code on line 5
>
> Call Stack:
> 0.0002 72232 1. {main}() Command line code:0
>

[snip]

> Könnte irgendjemand den ersten Teil reposten - Claus filtert mich, aber
> vielleicht hilft es seinem Verständnis.

Voilà!

Stefan Froehlich

unread,
Dec 16, 2010, 9:20:39 AM12/16/10
to
On Thu, 16 Dec 2010 12:07:50 Niels Braczek wrote:
> > Das Verstaendnis einer Klasse steigt (in meinen Augen) deutlich, wenn
> > man - so man wissen moechte, ob ihre Elemente sortierbar sind - sich
> > nicht erst ueberlegen muss, ob das durch eine der zahlreichen anderen
> > Schnittstellen impliziert wird, sondern diese Tatsache auch explizit
> > anfuehren kann.

> In dem Fall ist es eher sinnvoll, die Interfaces sauber getrennt zu
> halten; mir ist zudem noch nie ein Fall untergekommen, bei dem es
> sinnvoll gewesen wäre, Intefaces zu vererben.

Hm, siehe oben: "sortierbar" ist ein Interface, das fuer sich selbst
recht nuetzlich ist. Wenn nun eine Klasse z.B. Objekte enthaelt, die
in Select-Boxen angezeigt werden duerfen, genuegt sie einem anderen
Interface, wenn sie Suchergebnisse enthalten kann, wiederum einem
anderen - diese bauen aber beide darauf auf, dass die einzelnen
Elemente sortierbar sind und extenden deshalb das urspruengliche
Interface. Soweit nachvollziehbar?

> Bei

> class A {}
> class B extends A {}
> class C extends B {}

> sieht man C auch nicht ohne Weiteres die Abstammung von A an.

Stimmt. Klassen sind bei mir aber (primaer, weil es in PHP keine
multiple inheritance gibt) recht konsequent voneinander abgeleitet,
so in der Art von:

Fahrzeug -> Auto -> PKW
-> LKW
-> Flugzeug -> Segelflieger
-> Motorflieger

Interfaces haben hingegen thematisch gar nichts miteinander zu tun.
Eine Klasse CarManufacturer waere z.B. - alphabetisch - sortierbar,
koennte - bei der Erfassung eines Autos - in Select-Boxen
auftauchen, wuerde aber nie in Suchergebnissen vorkommen. Wenn ich
nun:

| class CarManufacturer implements SelectBox {
| }

schreibe, ist nicht intuitiv klar, dass Sortierbarkeit gegeben ist
(bei diesem simplen Beispiel natuerlich schon).

Dass ich jetzt zwar

| class CarManufacturer implements Sortable, SelectBox {
| }

schreiben darf, nicht aber

| class CarManufacturer implements SelectBox, Sortable {
| }

bringt mich natuerlich nicht um, hat mich aber erst einmal doch aus
dem Konzept gebracht. Und es wirft zumindestens die Frage auf, ob
man das Verhalten als Bug melden sollte.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Reizvoll bleibt reizvoll: Und dafuer Stefan!
(Sloganizer)

Stefan Froehlich

unread,
Dec 16, 2010, 9:24:16 AM12/16/10
to
On Thu, 16 Dec 2010 14:59:46 Torsten Zühlsdorff wrote:
> > Herrgottsackelzement, warum postest Du die Fehlermeldung nicht?

Dann bist Du wohl eh nicht die Zielgruppe des Postings - die paar
Sekunden fuer copy/paste des Quelltexts auf die Kommandozeile fallen
wohl kaum ins Gewicht, verglichen mit dem Nachdenken hinterher...
Fehler, Ursache und Workaround sind ja bereits bekannt, es geht um
Sinn und Unsinn des Verhaltens.



> Manchmal kann man sich auch anstellen:
> $ php -r "interface A {};
> > interface B extends A {};
> >
> > class C implements A, B {};
> > class D implements B, A {};"
> PHP Fatal error: Class D cannot implement previously implemented
> interface A in Command line code on line 5
> PHP Stack trace:
> PHP 1. {main}() Command line code:0
>
> Fatal error: Class D cannot implement previously implemented interface A
> in Command line code on line 5
>
> Call Stack:
> 0.0002 72232 1. {main}() Command line code:0

> Könnte irgendjemand den ersten Teil reposten - Claus filtert mich,


> aber vielleicht hilft es seinem Verständnis.

Laesst sich einrichten.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan, so zweisam wie das Denken.
(Sloganizer)

Niels Braczek

unread,
Dec 16, 2010, 10:30:05 AM12/16/10
to
Stefan Froehlich schrieb:

> On Thu, 16 Dec 2010 12:07:50 Niels Braczek wrote:

>> Bei
>
>> class A {}
>> class B extends A {}
>> class C extends B {}
>
>> sieht man C auch nicht ohne Weiteres die Abstammung von A an.
>
> Stimmt. Klassen sind bei mir aber (primaer, weil es in PHP keine
> multiple inheritance gibt) recht konsequent voneinander abgeleitet,
> so in der Art von:
>
> Fahrzeug -> Auto -> PKW
> -> LKW
> -> Flugzeug -> Segelflieger
> -> Motorflieger
>
> Interfaces haben hingegen thematisch gar nichts miteinander zu tun.

Dann sollten sie vielleicht auch nicht vermischt werden.

> Eine Klasse CarManufacturer waere z.B. - alphabetisch - sortierbar,
> koennte - bei der Erfassung eines Autos - in Select-Boxen
> auftauchen, wuerde aber nie in Suchergebnissen vorkommen.
> Wenn ich nun:
>
> | class CarManufacturer implements SelectBox {
> | }

Du meinst sicher Selectable statt Selectbox, oder? Interfaces mit
Adjektiven zu bezeichnen vermeidet Designfehler.

> schreibe, ist nicht intuitiv klar, dass Sortierbarkeit gegeben ist
> (bei diesem simplen Beispiel natuerlich schon).

Wieso *benötigt* eine Selectbox Sortierbarkeit?
=> Wenn eine Selectbox tatsächlich Sortierbarkeit *benötigt* (kann ich
mir nicht so recht vorstellen), ist es allerdings auch ohne weitere
Angaben klar, dass sie Sortierbarkeit enthält.

Stefan Froehlich

unread,
Dec 16, 2010, 5:16:30 PM12/16/10
to
On Thu, 16 Dec 2010 16:30:05 Niels Braczek wrote:
> > Interfaces haben hingegen thematisch gar nichts miteinander zu tun.

> Dann sollten sie vielleicht auch nicht vermischt werden.

Gerade darin sehe ich den hauptsaechlichen Sinn von Interfaces.

Die Klasse heisst "Auto", die Schnittstellen beziehen sich auf
Dinge, die man mit Objekten dieses Typs anstellen kann. Das koennen
autobezogene Sachen sein (vermieten, lackieren), aber auch
buchhalterische Dinge (Zeitwert ermitteln), oder vollkommen
abstrakte Sachen, die nur in der Programmierumgebung benoetigt
werden (also eben zB das Erscheinungsbild in selectboxen, die
Ermittlung von Tabelle und Primaerschluessel in der Datenbank).

Und genau so, wie eine Klasse Interfaces benoetigt, die thematisch
voellig anders angesiedelt sind, kann das auch fuer Interfaces
selbst zutreffen. Mit sinkendem Abstraktionsgrad wird das natuerlich
immer weniger.



> > Eine Klasse CarManufacturer waere z.B. - alphabetisch -
> > sortierbar, koennte - bei der Erfassung eines Autos - in
> > Select-Boxen auftauchen, wuerde aber nie in Suchergebnissen
> > vorkommen. Wenn ich nun:

> > | class CarManufacturer implements SelectBox {
> > | }

> Du meinst sicher Selectable statt Selectbox, oder? Interfaces mit
> Adjektiven zu bezeichnen vermeidet Designfehler.

Thema und Bezeichner sind hier rein fiktiv und daher willkuerlich
gewaehlt (wenn ich echte Bezeichner von mir verwende, fallen wieder
alle ob meiner fuerchterlichen Namenskonventionen in Ohnmacht - das
moechte ich nach Moeglichkeit vermeiden und lieber das Prinzip
dahinter diskutieren).

> > schreibe, ist nicht intuitiv klar, dass Sortierbarkeit gegeben
> > ist (bei diesem simplen Beispiel natuerlich schon).

> Wieso *benötigt* eine Selectbox Sortierbarkeit?

Ich musste mir auf die Schnelle ein halbwegs illustratives Beispiel
aus den Fingern saugen. Aber so abwegig finde ich das gar nicht: der
Inhalt eines <select> sollte tunlichst in einer fuer den Benutzer
genehmen Reihenfolge auftauchen (haeufig wird das alphabetisch
sein). Was liegt naeher, als diese Eigenschaft einer Klasse ueber
eine Schnittstelle herauszuziehen (z.B. ueber die Funktion
SortKey(), mit der der Sortierwert des jeweiligen Objekts erfragt
werden kann)?

Dort, wo die Selectbox erzeugt wird, brauche ich dann nur noch zu
pruefen, ob der Inhalt tatsaechlich der Schnittstelle genuegt, und
die Sache ist erledigt.

> => Wenn eine Selectbox tatsächlich Sortierbarkeit *benötigt* (kann
> ich mir nicht so recht vorstellen), ist es allerdings auch ohne
> weitere Angaben klar, dass sie Sortierbarkeit enthält.

In diese Richtung: ja.

Wenn man sich aber gerade in ganz anderem Kontext mit der Klasse
beschaeftigt und dafuer interessiert, ob sie (bereits) sortierbar
ist, erscheint es mir laestig, erst einmal ein Dutzend Interfaces
darauf zu ueberpruefen, ob eines davon seinerseits von "sortable"
abhaengig ist.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Für glückliche Stunden! Stefan - Glaube und Zahle!
(Sloganizer)

Niels Braczek

unread,
Dec 16, 2010, 5:55:55 PM12/16/10
to
Stefan Froehlich schrieb:

> On Thu, 16 Dec 2010 16:30:05 Niels Braczek wrote:

>> > Interfaces haben hingegen thematisch gar nichts miteinander zu tun.
>
>> Dann sollten sie vielleicht auch nicht vermischt werden.
>
> Gerade darin sehe ich den hauptsaechlichen Sinn von Interfaces.

Interfaces dienen dazu, eine *Minimum* an Anforderungen zu vereinbaren.

> Die Klasse heisst "Auto", die Schnittstellen beziehen sich auf
> Dinge, die man mit Objekten dieses Typs anstellen kann.

Es sollten Eigenschaften sein, die dem Objekt inhärent innewohnen.

> Das koennen
> autobezogene Sachen sein (vermieten, lackieren), aber auch
> buchhalterische Dinge (Zeitwert ermitteln), oder vollkommen
> abstrakte Sachen, die nur in der Programmierumgebung benoetigt
> werden (also eben zB das Erscheinungsbild in selectboxen, die
> Ermittlung von Tabelle und Primaerschluessel in der Datenbank).

Da muss ich widersprechen. "Auto" hat nichts davon zu wissen, wie es
dargestellt wird. Insbesondere weiß es nichts von Selectboxen. Auf einem
Finanzierungsantrag aus Papier funktionieren die nämlich sehr schlecht.

> Und genau so, wie eine Klasse Interfaces benoetigt, die thematisch
> voellig anders angesiedelt sind, kann das auch fuer Interfaces
> selbst zutreffen.

Das sehe ich eben noch nicht.

>> > schreibe, ist nicht intuitiv klar, dass Sortierbarkeit gegeben
>> > ist (bei diesem simplen Beispiel natuerlich schon).
>
>> Wieso *benötigt* eine Selectbox Sortierbarkeit?
>
> Ich musste mir auf die Schnelle ein halbwegs illustratives Beispiel
> aus den Fingern saugen. Aber so abwegig finde ich das gar nicht: der
> Inhalt eines <select> sollte tunlichst in einer fuer den Benutzer
> genehmen Reihenfolge auftauchen (haeufig wird das alphabetisch
> sein). Was liegt naeher, als diese Eigenschaft einer Klasse ueber
> eine Schnittstelle herauszuziehen (z.B. ueber die Funktion
> SortKey(), mit der der Sortierwert des jeweiligen Objekts erfragt
> werden kann)?

Ganz einfach: die ID/Label-Liste nach Label sortieren. Ohne Interface.

Das geht das Auto nichts an, wie ein select aussieht. Die Klasse
Selectbox akzeptiert ein beliebiges Object, das Selectable
implementiert. Wird zusätzlich das Sortable-Interface implementiert,
kann Selectbox die Liste sortiert darstellen, wenn nicht, dann eben
nicht. Sortable und Selectable haben *nichts* mieinander zu tun.

Für mich sieht das im Moment so aus, als ob du eine Gott-Klasse[1]
baust, also das Single-Responsibility-Prinzip verletzt, wodurch das
Problem überhaupt erst entsteht.

> Dort, wo die Selectbox erzeugt wird, brauche ich dann nur noch zu
> pruefen, ob der Inhalt tatsaechlich der Schnittstelle genuegt, und
> die Sache ist erledigt.

Selectbox muss ja auch nichts weiter wissen, als dass das übergebene
Objekt Selectable ist. Für die Sortierung würde eich einen Decorator in
Betracht ziehen.

[1] http://de.wikipedia.org/wiki/God_object

Stefan Froehlich

unread,
Dec 17, 2010, 3:33:47 AM12/17/10
to
On Thu, 16 Dec 2010 23:55:55 Niels Braczek wrote:
> >> > Interfaces haben hingegen thematisch gar nichts miteinander zu tun.
> >> Dann sollten sie vielleicht auch nicht vermischt werden.
> > Gerade darin sehe ich den hauptsaechlichen Sinn von Interfaces.

> Interfaces dienen dazu, eine *Minimum* an Anforderungen zu
> vereinbaren.

Ja, eben: jedes Interface fuer sich: jede Schnittstelle sorgt
dafuer, dass mit einem Objekt irgendetwas bestimmtes getan werden
kann. Aber die diversen "etwas" werden unmittelbar etwas miteinander
zu tun haben.

Wobei "Minium" durchaus breit gefasst sein kann: habe ich auf einer
hohen Abstraktionsebene eine Funktion, der Objekte unterschiedlichen
Typs zum Frass vorgeworfen werden sollen, dann erstelle ich ein
Interface mit genau den Methoden, die in dieser Funktion benoetigt
werden, um es fuer type hinting verwenden zu koennen. Und sobald
eine der hier angesprochenen Methoden in einem bereits vorhandenen
Interface auftaucht, entsteht eine Verkettung, wie wir sie gerade
diskutieren.



> > Die Klasse heisst "Auto", die Schnittstellen beziehen sich auf
> > Dinge, die man mit Objekten dieses Typs anstellen kann.

> Es sollten Eigenschaften sein, die dem Objekt inhärent innewohnen.

Das ist allerdings eine sehr schwammige Formulierung. Nehmen wir
z.B. einen Report XY, den eine Applikation erzeugt und der (siehe
oben) mit Objekten gefuettert werden kann, z.B. unter anderem auch
mit Autos. Dann ist "es kann ein Bericht erstellt werden" sicherlich
keine inhaerente Eigenschaft des Objekts, sehr wohl aber bekaeme es
bei mir die Schnittstelle reportable_XY verpasst (und die kann
wiederum, faellt mir beim Schreiben gerade auf, andere Eigenschaften
geringerer Abstraktionsebene voraussetzen, fuer die es ohnehin
bereits vorhandene Schnittstellen gibt).



> "Auto" hat nichts davon zu wissen, wie es dargestellt wird.
> Insbesondere weiß es nichts von Selectboxen. Auf einem
> Finanzierungsantrag aus Papier funktionieren die nämlich sehr
> schlecht.

Ja, aber wenn es das Auto nicht weiss, wer dann? (Es weiss auch
nichts darueber, wie es dargestellt wird, aber es wird ueber die
Interface-Methode gefragt: wie heisst Du dem Anwender gegenueber und
welche ID besitzt Du?).

Die Darstellung uebernimmt in letzter Konsequenz eine Klasse fuer
Selectboxen, die aus einer Klasse fuer allgemeine Formularelemente
abgeleitet ist. DIE weiss aber nichts ueber Autos (sondern sie wird
mit einem Array von erst einmal beliebigen Objekten gefuettert), und
genau dafuer gibt es nun das Interface mit einem definierten Weg,
die gewuenschten Informationen in Erfahrung zu bringen.



> >> Wieso *benötigt* eine Selectbox Sortierbarkeit?

> > der Inhalt eines <select> sollte tunlichst in einer fuer den


> > Benutzer genehmen Reihenfolge auftauchen (haeufig wird das
> > alphabetisch sein). Was liegt naeher, als diese Eigenschaft

> > einer Klasse ueber eine Schnittstelle herauszuziehen [...]

> Ganz einfach: die ID/Label-Liste nach Label sortieren. Ohne
> Interface.

Nun habe ich aber ein Objekt der Klasse "Mitarbeiter", das von sich
selbst behauptet, dem Benutzer als "Vorname Nachname" praesentiert,
aber nach "Nachname Vorname" sortiert werden zu wollen. Ja, das ist
in meinen Augen ein bisschen gaga, aber es gibt Leute, die sich das
tatsaechlich so wuenschen. Wer, wenn nicht die Klasse "Mitarbeiter"
soll ueber solche Dinge Bescheid wissen?

Oder, anderes Beispiel: hierarchisch sortierbare Objekte, die dann
nur innerhalb einer Hierarchiestufe alphabetisch sortiert werden.

> Das geht das Auto nichts an, wie ein select aussieht. Die Klasse
> Selectbox akzeptiert ein beliebiges Object, das Selectable
> implementiert. Wird zusätzlich das Sortable-Interface implementiert,
> kann Selectbox die Liste sortiert darstellen, wenn nicht, dann eben
> nicht.

Gut, so laesst sich das in diesem Fall natuerlich auch machen. Ich
habe auch Faelle, wo das untergeordnete Interace _immer_ benoetigt
wird, aber die sind zu spezifisch, als dass ich sie hier kurz
skizzieren koennte.

> Für mich sieht das im Moment so aus, als ob du eine Gott-Klasse[1]
> baust,

Dafuer sind es in der Realitaet deutlich zu viele Klassen :-)

> Selectbox muss ja auch nichts weiter wissen, als dass das übergebene
> Objekt Selectable ist. Für die Sortierung würde eich einen Decorator in
> Betracht ziehen.

Hm. Damit bin ich nicht wirklich gluecklich geworden, sobald Multitons
ins Spiel gekommen sind (wegen nicht-oeffentlicher Konstruktoren,
bilde ich mir ein). Ist aber schon etwas laenger her, genau habe ich
das nicht mehr im Kopf.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Für kleine Genießer - flörten mit Stefan!
(Sloganizer)

Niels Braczek

unread,
Dec 17, 2010, 11:20:39 AM12/17/10
to
Stefan Froehlich schrieb:

> On Thu, 16 Dec 2010 23:55:55 Niels Braczek wrote:

>> Interfaces dienen dazu, eine *Minimum* an Anforderungen zu
>> vereinbaren.
>
> Ja, eben: jedes Interface fuer sich: jede Schnittstelle sorgt
> dafuer, dass mit einem Objekt irgendetwas bestimmtes getan werden
> kann. Aber die diversen "etwas" werden unmittelbar etwas miteinander
> zu tun haben.

Das ist im ersten Ansatz nicht relevant.

> Wobei "Minium" durchaus breit gefasst sein kann: habe ich auf einer
> hohen Abstraktionsebene eine Funktion, der Objekte unterschiedlichen
> Typs zum Frass vorgeworfen werden sollen, dann erstelle ich ein
> Interface mit genau den Methoden, die in dieser Funktion benoetigt
> werden, um es fuer type hinting verwenden zu koennen.

Korrekt.

> Und sobald
> eine der hier angesprochenen Methoden in einem bereits vorhandenen
> Interface auftaucht, entsteht eine Verkettung, wie wir sie gerade
> diskutieren.

Das sehe ich eben (noch) nicht.

>> > Die Klasse heisst "Auto", die Schnittstellen beziehen sich auf
>> > Dinge, die man mit Objekten dieses Typs anstellen kann.
>
>> Es sollten Eigenschaften sein, die dem Objekt inhärent innewohnen.
>
> Das ist allerdings eine sehr schwammige Formulierung. Nehmen wir
> z.B. einen Report XY, den eine Applikation erzeugt und der (siehe
> oben) mit Objekten gefuettert werden kann, z.B. unter anderem auch
> mit Autos. Dann ist "es kann ein Bericht erstellt werden" sicherlich
> keine inhaerente Eigenschaft des Objekts, sehr wohl aber bekaeme es
> bei mir die Schnittstelle reportable_XY verpasst (und die kann
> wiederum, faellt mir beim Schreiben gerade auf, andere Eigenschaften
> geringerer Abstraktionsebene voraussetzen, fuer die es ohnehin
> bereits vorhandene Schnittstellen gibt).

Eine Report-Klasse wird von jedem verarbeitbaren Objekt nichts anderes
wissen wollen, als
- welche Properties stehen zur Verfügung?
- welche Werte haben diese?
also etwa

interface Reportable {
/** @return array assoc list of requested or all (default) props */
public function getProperties( array $propertyList=array() ) {}
}

Sortierbarkeit ist hier zwar vielleicht nützlich, aber keineswegs notwendig.


>> "Auto" hat nichts davon zu wissen, wie es dargestellt wird.
>> Insbesondere weiß es nichts von Selectboxen. Auf einem
>> Finanzierungsantrag aus Papier funktionieren die nämlich sehr
>> schlecht.
>
> Ja, aber wenn es das Auto nicht weiss, wer dann? (Es weiss auch
> nichts darueber, wie es dargestellt wird, aber es wird ueber die
> Interface-Methode gefragt: wie heisst Du dem Anwender gegenueber und
> welche ID besitzt Du?).

Genau, es implementiert Selectable:

interface Selectable {
public function getId() {}
public function getLabel() {}
}

Auch hier ist zB. Sortierbarkeit nützlich, aber nicht notwendig.

> Die Darstellung uebernimmt in letzter Konsequenz eine Klasse fuer
> Selectboxen, die aus einer Klasse fuer allgemeine Formularelemente
> abgeleitet ist. DIE weiss aber nichts ueber Autos (sondern sie wird
> mit einem Array von erst einmal beliebigen Objekten gefuettert), und
> genau dafuer gibt es nun das Interface mit einem definierten Weg,
> die gewuenschten Informationen in Erfahrung zu bringen.

Ja, das leistet das o.a. Interface.

>> > der Inhalt eines <select> sollte tunlichst in einer fuer den
>> > Benutzer genehmen Reihenfolge auftauchen (haeufig wird das
>> > alphabetisch sein). Was liegt naeher, als diese Eigenschaft
>> > einer Klasse ueber eine Schnittstelle herauszuziehen [...]
>
>> Ganz einfach: die ID/Label-Liste nach Label sortieren. Ohne
>> Interface.
>
> Nun habe ich aber ein Objekt der Klasse "Mitarbeiter", das von sich
> selbst behauptet, dem Benutzer als "Vorname Nachname" praesentiert,
> aber nach "Nachname Vorname" sortiert werden zu wollen. Ja, das ist
> in meinen Augen ein bisschen gaga, aber es gibt Leute, die sich das
> tatsaechlich so wuenschen. Wer, wenn nicht die Klasse "Mitarbeiter"
> soll ueber solche Dinge Bescheid wissen?

Wenn, dann nicht Employee, sondern EmployeeCollection; ein einzelnes
Objekt kann ja nicht sortiert werden.

interface Sortable {
public function setSortCriterion( array $propertyList ) {}
}

Die Collection liefer die Einträge dann gemäß der eingestellten
Sortierkriterien.

> Oder, anderes Beispiel: hierarchisch sortierbare Objekte, die dann
> nur innerhalb einer Hierarchiestufe alphabetisch sortiert werden.

Mit dem o.a. Interface ist das möglich, wenn mehrere Properties
angegeben werden.

>> Das geht das Auto nichts an, wie ein select aussieht. Die Klasse
>> Selectbox akzeptiert ein beliebiges Object, das Selectable
>> implementiert. Wird zusätzlich das Sortable-Interface implementiert,
>> kann Selectbox die Liste sortiert darstellen, wenn nicht, dann eben
>> nicht.
>
> Gut, so laesst sich das in diesem Fall natuerlich auch machen. Ich
> habe auch Faelle, wo das untergeordnete Interace _immer_ benoetigt
> wird, aber die sind zu spezifisch, als dass ich sie hier kurz
> skizzieren koennte.

Das ist egal. Daraus ergibt sich kein Grund, ein Interface von einem
anderen abzuleiten. Selbst wenn es Methoden gibt (zB. getId()), die von
mehreren Interfaces verlangt werden, haben die Interfaces keine
Verwandtschaft.

>> Für mich sieht das im Moment so aus, als ob du eine Gott-Klasse[1]
>> baust,
>
> Dafuer sind es in der Realitaet deutlich zu viele Klassen :-)

Es gibt ja auch noch die Vielgötterei ;-)

>> Selectbox muss ja auch nichts weiter wissen, als dass das übergebene

>> Objekt Selectable ist. Für die Sortierung würde ich einen Decorator in


>> Betracht ziehen.
>
> Hm. Damit bin ich nicht wirklich gluecklich geworden, sobald Multitons
> ins Spiel gekommen sind (wegen nicht-oeffentlicher Konstruktoren,
> bilde ich mir ein). Ist aber schon etwas laenger her, genau habe ich
> das nicht mehr im Kopf.

Das wäre kein Hinderungsgrund, da ein Decorator immer ein bereits
instantiiertes Object schmückt. Ich benutze Decoratoren (bzw. Proxies)
zB. für ein Datenbank-Singleton, um Zugriffsrechte zu beeinflussen.

Ulf [Kado] Kadner

unread,
Dec 20, 2010, 10:15:53 AM12/20/10
to
Am 17.12.2010 17:20, schrieb Niels Braczek:

> Wenn, dann nicht Employee, sondern EmployeeCollection; ein einzelnes
> Objekt kann ja nicht sortiert werden.

Du meinst hier doch sicher das ein einzelner Mitarbeiter (Employee) als
logische EInheit nicht sortiert werden kann?

Ein Objekt kann wie in Deinem Beispiel als Collection natürlich sortiert
werden wenn es logisch und zuordenbar ein Container (List, Collection
etc.) für andere Objekte, in dynamischer oder fester Menge darstellt.

Nur um da evtl. Mißverständnissen vorzubeugen…

Ansonsten ACK.

MfG, Ulf

--
Mit 70 muss man damit rechnen, aus biologischen Gründen
vertragsbrüchig zu werden. [Loriot]

Niels Braczek

unread,
Dec 20, 2010, 10:35:05 AM12/20/10
to
Ulf [Kado] Kadner schrieb:

> Am 17.12.2010 17:20, schrieb Niels Braczek:
>
>> Wenn, dann nicht Employee, sondern EmployeeCollection; ein einzelnes
>> Objekt kann ja nicht sortiert werden.
>
> Du meinst hier doch sicher das ein einzelner Mitarbeiter (Employee) als
> logische EInheit nicht sortiert werden kann?

Ja, genau.

> Ein Objekt kann wie in Deinem Beispiel als Collection natürlich sortiert
> werden wenn es logisch und zuordenbar ein Container (List, Collection
> etc.) für andere Objekte, in dynamischer oder fester Menge darstellt.

Das ist im Prinzip zar richtig, Employee ist aber nur ein Item, keine
Collection. Deshalb gehört dort auch keine Information bzgl. Sortierung
hinein. Die Collection hingegen weiß sehr wohl, ob die Mitarbeiter nach
Vorname, Nachname, Gehaltsstufe ode Schuhgröße sortiert sind/sein sollen.

Ulf [Kado] Kadner

unread,
Dec 20, 2010, 10:56:47 AM12/20/10
to
Am 20.12.2010 16:35, schrieb Niels Braczek:
> Ulf [Kado] Kadner schrieb:
>> Am 17.12.2010 17:20, schrieb Niels Braczek:
>>
>>> Wenn, dann nicht Employee, sondern EmployeeCollection; ein einzelnes
>>> Objekt kann ja nicht sortiert werden.
>>
>> Du meinst hier doch sicher das ein einzelner Mitarbeiter (Employee) als
>> logische EInheit nicht sortiert werden kann?
>
> Ja, genau.

OK :-)

>> Ein Objekt kann wie in Deinem Beispiel als Collection natürlich sortiert
>> werden wenn es logisch und zuordenbar ein Container (List, Collection
>> etc.) für andere Objekte, in dynamischer oder fester Menge darstellt.
>
> Das ist im Prinzip zar richtig, Employee ist aber nur ein Item, keine
> Collection.

Ja naja ich hab das erst irgendwie so verstanden das Du garnicht auf
eins von beiden beziehst (wohl weil Du das generelle "Objekt" verwendet
hast)

> Deshalb gehört dort auch keine Information bzgl. Sortierung
> hinein.

> Die Collection hingegen weiß sehr wohl, ob die Mitarbeiter nach
> Vorname, Nachname, Gehaltsstufe ode Schuhgröße sortiert sind/sein sollen.

Meine Rede… :-x

MfG, Ulf

--
Eines Tages wird man offiziell zugeben müssen, daß das, was
wir Wirklichkeit getauft haben, eine noch größere Illusion
ist als die Welt des Traumes. [Salvador Dalí]

Stefan Froehlich

unread,
Jan 29, 2011, 11:02:30 AM1/29/11
to
So. Einige Feiertage und Wochen mit zu hoher Auslastung spaeter
versuche ich, die fuer mich interessanten Aspekte der Diskussion
fortzusetzen. Mal sehen.

On Fri, 17 Dec 2010 17:20:39 Niels Braczek wrote:
> >> "Auto" hat nichts davon zu wissen, wie es dargestellt wird.
> >> Insbesondere weiß es nichts von Selectboxen.

> > Ja, aber wenn es das Auto nicht weiss, wer dann? (Es weiss auch


> > nichts darueber, wie es dargestellt wird, aber es wird ueber die
> > Interface-Methode gefragt: wie heisst Du dem Anwender gegenueber
> > und welche ID besitzt Du?).

> Genau, es implementiert Selectable:
>
> interface Selectable {
> public function getId() {}
> public function getLabel() {}
> }

Ja, das entspricht ziemlich genau der wesentlicheren Haelfte meines
Interfaces "Selectable".



> > Nun habe ich aber ein Objekt der Klasse "Mitarbeiter", das von
> > sich selbst behauptet, dem Benutzer als "Vorname Nachname"
> > praesentiert, aber nach "Nachname Vorname" sortiert werden zu

> > wollen. [...]

> Wenn, dann nicht Employee, sondern EmployeeCollection; ein
> einzelnes Objekt kann ja nicht sortiert werden.

Das setzt die Existenz einer derartigen (getrennten) Klasse voraus.
Weil fuer sonstige Funktionalitaet einer Collection oft kein Bedarf
da ist, uebergebe ich dann einfach Arrays mit den Objekten. Das da:



> interface Sortable {
> public function setSortCriterion( array $propertyList ) {}
> }

...steckt dann halt mit im Basisobjekt. Ich sehe das nicht als
uebermaessig grosses Problem und vermeide eine Klasse samt dem
Overhead fuer deren Verwendung, _nur_ um die Sortierreihenfolge am
kanonisch richtigen Ort definiert zu haben.

Eleganter waere es, zugegeben.

> >> Selectbox muss ja auch nichts weiter wissen, als dass das
> >> übergebene Objekt Selectable ist. Für die Sortierung würde ich
> >> einen Decorator in Betracht ziehen.

Hm. Decorator. Im Prinzip waere das ja auch ganz huebsch, um Code,
der nur isoliert fuer einen bestimmten Zweck benoetigt wird, in eine
eigene Klasse verlagern zu koennen und damit die Klassen schlanker
zu halten. Beim Sortieren sind wir uns ueber den Ort der
Unterbringung uneinig, daher ueberlege ich mir exemplarisch, die
Funktionalitaet "selectable" in einen Decorator auszulagern; es
sollte ja prinzipiell auch nichts dagegen sprechen, und das Prinzip
bleibt das gleiche. Nehmen wir wieder das Beispiel Auto - nach
Lehrbuch implementiert habe ich dann:

| interface ISelectbox {}
| interface ICar {}
| class CarObject implements ICar {}
| abstract class CarDecorator implements ICar {}
| class CarSelectboxDecorator implements Car extends ISelectbox {}

Nun gibt mir "$cars = CarObject::searchByBrand('bmw')" eine Liste
von Objekten vom Typ CarObject zurueck. Diese wandert in ein
Template und danach weiter in den Konstruktor z.B. der Klasse
Selectbox. Genau _dort_ haette ich nun gerne die Funktionalitaet zur
Verfuegung, aber: wer dekoriert mir an welcher Stelle meine Objekte?

Vor dem Aufruf des Templates weiss keiner, was mit den Objekten
spaeter geschehen wird, innerhalb von Selectbox weiss keiner, was
das fuer Objekte sind, die dargestellt werden sollen, und wie deren
Decorator gefunden werden kann; im Template selbst bin ich zum einen
mit der Menge Code einer Iteration ueber die Objektliste samt der
Umetikettierung der einzelnen Objekte alles andere als gluecklich,
und zum anderen ist auch ueberhaupt nicht garantiert, dass jedes
Template stets weiss, welche Art von Objekten es gerade darstellt.

Dazu kommt die unangenehme Tatsache, dass ICar{} und CarDecorator{}
alle oeffentlichen Methoden von CarObject{} bereitstellen muessen.
Das ist beim klassischen Anwendungsfall (Wikipedia: Window), wo man
eine recht genau definierte Basisklasse mit kleinem und vor allem
stabilen Funktionsumfang hat, kein grosses Thema. Einige meiner
Klassen sind das genaue Gegenteil davon: die Spezifikation ist eher
instabil (durch wachsende Benutzeranforderungen), und die Zahl
der Methoden ist hoch - das dann alles 3x vorhalten?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Entbehrlich? Entbehrlicher als Stefan!? Was zu beweisen wäre!
(Sloganizer)

Niels Braczek

unread,
Jan 29, 2011, 10:01:27 PM1/29/11
to
Stefan Froehlich schrieb:

>> Wenn, dann nicht Employee, sondern EmployeeCollection; ein
>> einzelnes Objekt kann ja nicht sortiert werden.
>
> Das setzt die Existenz einer derartigen (getrennten) Klasse voraus.
> Weil fuer sonstige Funktionalitaet einer Collection oft kein Bedarf
> da ist, uebergebe ich dann einfach Arrays mit den Objekten. Das da:

Aus dieser "Vereinfachung" resultiert wahrscheinlich dein Problem. Wenn
du Collections auch als solche implementierst, müssen Sortable und
Selectable nichts anderes kennen. Wobei die beiden Letzteren mit Fug und
Recht als inhärente Eigenschaften einer Collection angesehen werden
können, deren Funktion also direkt in Collection implementiert werden kann.

>> interface Sortable {
>> public function setSortCriterion( array $propertyList ) {}
>> }
>

> ....steckt dann halt mit im Basisobjekt. Ich sehe das nicht als


> uebermaessig grosses Problem und vermeide eine Klasse samt dem
> Overhead fuer deren Verwendung, _nur_ um die Sortierreihenfolge am
> kanonisch richtigen Ort definiert zu haben.
>
> Eleganter waere es, zugegeben.

Und einfacher in Entwurf, Pflege und Erweiterung.

>> >> Selectbox muss ja auch nichts weiter wissen, als dass das
>> >> übergebene Objekt Selectable ist. Für die Sortierung würde ich
>> >> einen Decorator in Betracht ziehen.
>
> Hm. Decorator. Im Prinzip waere das ja auch ganz huebsch, um Code,
> der nur isoliert fuer einen bestimmten Zweck benoetigt wird, in eine
> eigene Klasse verlagern zu koennen und damit die Klassen schlanker
> zu halten. Beim Sortieren sind wir uns ueber den Ort der
> Unterbringung uneinig, daher ueberlege ich mir exemplarisch, die
> Funktionalitaet "selectable" in einen Decorator auszulagern; es
> sollte ja prinzipiell auch nichts dagegen sprechen, und das Prinzip
> bleibt das gleiche. Nehmen wir wieder das Beispiel Auto - nach
> Lehrbuch implementiert habe ich dann:
>
> | interface ISelectbox {}
> | interface ICar {}
> | class CarObject implements ICar {}
> | abstract class CarDecorator implements ICar {}
> | class CarSelectboxDecorator implements Car extends ISelectbox {}
>
> Nun gibt mir "$cars = CarObject::searchByBrand('bmw')" eine Liste
> von Objekten vom Typ CarObject zurueck. Diese wandert in ein
> Template und danach weiter in den Konstruktor z.B. der Klasse
> Selectbox. Genau _dort_ haette ich nun gerne die Funktionalitaet zur
> Verfuegung, aber: wer dekoriert mir an welcher Stelle meine Objekte?

CarObject::searchByBrand('bmw') sollte eine Collection von
CarObject-Objekten liefern.
Selectbox kann mit Collections umgehen; die zu betrachtende Collection
kann im Constructor übergeben werden.

Dein Sortable-Interface geht in Collection auf:

abstract Collection implements Iterator, ArrayAccess
{
private $items = array();
public function addItem( $item ) {}
public function removeItem( $item ) {}


public function setSortCriterion( array $propertyList ) {}

public function getSelectOptions() {}
public function getIterator() {}
...
}

Damit hast du alles, was zu einer Collection gehört, zentral an einer
Stelle festgelegt. abstract deswegen, weil viele der Methoden unabhängig
vom gesammelten Typ sind und daher allgemein formuliert werden können.

> Vor dem Aufruf des Templates weiss keiner, was mit den Objekten
> spaeter geschehen wird, innerhalb von Selectbox weiss keiner, was
> das fuer Objekte sind, die dargestellt werden sollen, und wie deren
> Decorator gefunden werden kann; im Template selbst bin ich zum einen
> mit der Menge Code einer Iteration ueber die Objektliste samt der
> Umetikettierung der einzelnen Objekte alles andere als gluecklich,
> und zum anderen ist auch ueberhaupt nicht garantiert, dass jedes
> Template stets weiss, welche Art von Objekten es gerade darstellt.

Mit der Collection muss es das meistens auch nicht.

> Dazu kommt die unangenehme Tatsache, dass ICar{} und CarDecorator{}
> alle oeffentlichen Methoden von CarObject{} bereitstellen muessen.

Das wäre gerade in PHP ja gar kein Problem: __call bzw. __callStatic
erlauben die ganz einfach, Methodenaufrufe zu delegieren.

> Das ist beim klassischen Anwendungsfall (Wikipedia: Window), wo man
> eine recht genau definierte Basisklasse mit kleinem und vor allem
> stabilen Funktionsumfang hat, kein grosses Thema. Einige meiner
> Klassen sind das genaue Gegenteil davon: die Spezifikation ist eher
> instabil (durch wachsende Benutzeranforderungen), und die Zahl
> der Methoden ist hoch - das dann alles 3x vorhalten?

Egal - durch die magischen Methoden erweitert sich der Decorator
praktisch selbst.

Stefan Froehlich

unread,
Jan 31, 2011, 8:52:25 AM1/31/11
to
On Sun, 30 Jan 2011 04:01:27 Niels Braczek wrote:
> Wenn du Collections auch als solche implementierst, müssen
> Sortable und Selectable nichts anderes kennen. Wobei die beiden
> Letzteren mit Fug und Recht als inhärente Eigenschaften einer
> Collection angesehen werden können, deren Funktion also direkt in
> Collection implementiert werden kann.

Danke fuer die Denkanstoesse, das setzt gerade einiges in Bewegung.
Ob jede Collection sortierbar und als Liste anzeigbar sein muss, bin
ich mir noch nicht sicher - aber das ist dann durch eine Kaskade der
Form:

| abstract class Collection {}
| abstract class SortableCollection extends Collection {}
| abstract class SelectableCollection extends SortableCollection {}

...relativ einfach darstellbar, und auch die Nutzklassen
(z.B. CarCollection) koennen bei Bedarf relativ leicht hochgehoben
werden.



> > Nehmen wir wieder das Beispiel Auto - nach Lehrbuch
> > implementiert habe ich dann:

> > | interface ISelectbox {}
> > | interface ICar {}
> > | class CarObject implements ICar {}
> > | abstract class CarDecorator implements ICar {}
> > | class CarSelectboxDecorator implements Car extends ISelectbox {}

> > Nun gibt mir "$cars = CarObject::searchByBrand('bmw')" eine
> > Liste von Objekten vom Typ CarObject zurueck. Diese wandert in
> > ein Template und danach weiter in den Konstruktor z.B. der
> > Klasse Selectbox. Genau _dort_ haette ich nun gerne die
> > Funktionalitaet zur Verfuegung, aber: wer dekoriert mir an
> > welcher Stelle meine Objekte?

> CarObject::searchByBrand('bmw') sollte eine Collection von
> CarObject-Objekten liefern. Selectbox kann mit Collections
> umgehen; die zu betrachtende Collection kann im Constructor
> übergeben werden.

Auf diese Weise eruebrigt sich fuer diesen Fall der Decorator
komplett, und damit natuerlich auch die Frage, wo er zur Anwendung
kommen soll :-)



> Dein Sortable-Interface geht in Collection auf:
> abstract Collection implements Iterator, ArrayAccess {}

Das Interface "Iterator" benoetige ich fuer foreach(), klar. Was
konkret bringt mir der Bezug auf "ArrayAccess"?

[Bei der Gelegenheit habe ich dafuer aber etwas anderes entdeckt,
das mein OP in diesem Thread wiederum rechtfertigt: in der SPL gibt
es die Interfaces "RecursiveIterator" und "SeekableIterator", die
beide das Interface "Iterator" erweitern - es gibt also doch auch
noch andere Menschen, die solche Konstrukte verwenden]

> > Dazu kommt die unangenehme Tatsache, dass ICar{} und
> > CarDecorator{} alle oeffentlichen Methoden von CarObject{}
> > bereitstellen muessen.

> Das wäre gerade in PHP ja gar kein Problem: __call bzw.
> __callStatic erlauben die ganz einfach, Methodenaufrufe zu
> delegieren.

__callStatic steht mir erst in ein paar Wochen zur Verfuegung
(sobald Debian/Squeeze fertig ist), aber prinzipiell stimmt das
natuerlich. Allerdings gilt das nur fuer den Decorator und nicht
fuer das Interface.



> > die Zahl der Methoden ist hoch - das dann alles 3x vorhalten?

> Egal - durch die magischen Methoden erweitert sich der Decorator
> praktisch selbst.

Ok, also 2x vorhalten. Im konkreten Fall eruebrigt sich das aber in
Ermangelung eines Decorators nun vermutlich ohnehin.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan - nicht einer treibt hässlicher und auch nicht ungewöhnlicher.
(Sloganizer)

Niels Braczek

unread,
Jan 31, 2011, 11:22:44 PM1/31/11
to
Stefan Froehlich schrieb:

> On Sun, 30 Jan 2011 04:01:27 Niels Braczek wrote:

>> Dein Sortable-Interface geht in Collection auf:
>> abstract Collection implements Iterator, ArrayAccess {}
>
> Das Interface "Iterator" benoetige ich fuer foreach(), klar. Was
> konkret bringt mir der Bezug auf "ArrayAccess"?

ArrayAccess ist bei Collections sehr nützlich bzw. bequem; es erlaubt
auf die Elemente über einen Index mit Array_Notation zuzugreifen.

$bmw = $carCollection[$id];

Stefan Froehlich

unread,
Feb 4, 2011, 8:25:33 AM2/4/11
to
On Tue, 01 Feb 2011 05:22:44 Niels Braczek wrote:
> > Das Interface "Iterator" benoetige ich fuer foreach(), klar. Was
> > konkret bringt mir der Bezug auf "ArrayAccess"?

> ArrayAccess ist bei Collections sehr nützlich bzw. bequem; es
> erlaubt auf die Elemente über einen Index mit Array_Notation
> zuzugreifen.

> $bmw = $carCollection[$id];

Huebsch. (Noch huebscher waere es, wenn auch Dinge wie count() damit
funktionieren wuerden, aber wahrscheinlich bin ich unbescheiden).

Zum Thema Collection: bis dato gibt es das schon diskutierte
Interface ISortable, mit dem die Objekte selbst festlegen koennen,
wo und wie sie einsortiert werden wollen. Skizziert in etwa so:

| abstract class A implements ISortable {
| abstract public function SortValue();
| }
| class A1 extends A {
| public function SortValue() { return $this->propA; }
| }
| class A2 extends A {
| public function SortValue() { return $this->propB; }
| }
|
| $a = array();
| $a[] = new A1();
| $a[] = new A2();
| sort($a);

Die Idee der Collections war nun, die Information ueber
Sortierschluessel aus der Klasse zu entfernen, weil sie dort von der
Logik her schlecht aufgehoben ist. Bei einfachen Klassen ist das ja
auch ganz nett. Bei Konstrukten wie diesen wird daraus nun aber:

| class CollectionA extends Collection {
| public function addItem(A $item) { }
| public function sort() { }
| }
|
| abstract class A {}
| class A1 extends A {}
| class A2 extends A {}
|
| $a = new CollectionA();
| $a->addItem(new A1());
| $a->addItem(new A2());
| $a->sort();

Es erscheint mir ein wenig ungeschickt, alle Sortierverfahren in
CollectionA{} abzulegen - dann habe ich sie zwar aus der jeweiligen
Klassendefinition herausgeloest, dafuer aber klassenuebergreifend in
einer Datei zusammengefasst; in meinen Augen ist das noch schlimmer,
als vorher. CollectionA in Subklassen aufzuteilen hat aber
andererseits auch keinen Sinn, weil der Witz ja gerade darin
besteht, die unterschiedlichen Objekte in _einer_ Collection
zusammenfassen zu koennen.

Gibt es dafuer irgendein Patentrezept?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Der zweisame Liebhaber braucht Stefan. Und Du?
(Sloganizer)

Benjamin Zikarsky

unread,
Feb 4, 2011, 9:26:33 AM2/4/11
to
Am 04.02.2011 14:25, schrieb Stefan Froehlich:
> On Tue, 01 Feb 2011 05:22:44 Niels Braczek wrote:
>> ArrayAccess ist bei Collections sehr nützlich bzw. bequem; es
>> erlaubt auf die Elemente über einen Index mit Array_Notation
>> zuzugreifen.
>
>> $bmw = $carCollection[$id];
>
> Huebsch. (Noch huebscher waere es, wenn auch Dinge wie count() damit
> funktionieren wuerden, aber wahrscheinlich bin ich unbescheiden).

Wenn du das Interface `\Countable` implementierst, kannst du auch
`count($carCollection)` aufrufen. Siehe
<http://www.php.net/~helly/php/ext/spl/interfaceCountable.html>

Benjamin

Niels Braczek

unread,
Feb 4, 2011, 10:27:25 AM2/4/11
to
Stefan Froehlich schrieb:

> On Tue, 01 Feb 2011 05:22:44 Niels Braczek wrote:

> Huebsch. (Noch huebscher waere es, wenn auch Dinge wie count() damit
> funktionieren wuerden, aber wahrscheinlich bin ich unbescheiden).

Das gibt es: http://de.php.net/manual/en/class.countable.php

> Zum Thema Collection: bis dato gibt es das schon diskutierte
> Interface ISortable, mit dem die Objekte selbst festlegen koennen,
> wo und wie sie einsortiert werden wollen. Skizziert in etwa so:
>
> | abstract class A implements ISortable {
> | abstract public function SortValue();
> | }
> | class A1 extends A {
> | public function SortValue() { return $this->propA; }
> | }
> | class A2 extends A {
> | public function SortValue() { return $this->propB; }
> | }
> |
> | $a = array();
> | $a[] = new A1();
> | $a[] = new A2();
> | sort($a);

Erstens kann sort() hier nicht greifen; du müsstest usort mit Callback
verwenden. Damit wäre die skizzierte Einfachheit dahin:

$a = array();
$a[] = new A1();
$a[] = new A2();

usort($a, 'compareAObjects');

Zweitens hat das Konstrukt zur Folge, dass A1 die Reifengröße und A2 die
Farbe der Innenausstattung als Sortierkriterium liefern könnten - was
völlig sinnfrei ist.

> Die Idee der Collections war nun, die Information ueber
> Sortierschluessel aus der Klasse zu entfernen, weil sie dort von der
> Logik her schlecht aufgehoben ist. Bei einfachen Klassen ist das ja
> auch ganz nett. Bei Konstrukten wie diesen wird daraus nun aber:
>
> | class CollectionA extends Collection {
> | public function addItem(A $item) { }
> | public function sort() { }
> | }
> |
> | abstract class A {}
> | class A1 extends A {}
> | class A2 extends A {}
> |
> | $a = new CollectionA();
> | $a->addItem(new A1());
> | $a->addItem(new A2());
> | $a->sort();
>
> Es erscheint mir ein wenig ungeschickt, alle Sortierverfahren in
> CollectionA{} abzulegen - dann habe ich sie zwar aus der jeweiligen
> Klassendefinition herausgeloest, dafuer aber klassenuebergreifend in
> einer Datei zusammengefasst; in meinen Augen ist das noch schlimmer,
> als vorher. CollectionA in Subklassen aufzuteilen hat aber
> andererseits auch keinen Sinn, weil der Witz ja gerade darin
> besteht, die unterschiedlichen Objekte in _einer_ Collection
> zusammenfassen zu koennen.

Die unterschiedlichen Objekte müssen ja immer einen gemeinsamen Nenner
haben. Bei deinem "einfachen" Konzept oben müssen sie von
compareAObjects() gleichbehandelt werden können, bei dem
Collection-Ansatz eben von der Collection. Deshalb sind alle Objekte in
der CollectionA von A abgeleitet. In der Hinsicht gibt es also keinen
Unterschied, außer dass die Collection die Funktionalität sauber
kapselt. Zusammen mit dem ArrayAccess-Interface kommst du so sehr nah an
die gewünschte Einfachheit:

$a = new CollectionA();


$a[] = new A1();
$a[] = new A2();

$a->sort();

CollectionA::sort() kannst du dann noch so auslegen, dass du die
Eigenschaft mitgeben kannst, nach der sortiert werden soll. Die
Sortierrichtung wäre ein weiterer Parameter. Beide Parameter werden idR
mit sinnvollen Defaultwerten belegt und optional gemacht.

Helmut Chang

unread,
Feb 4, 2011, 12:26:40 PM2/4/11
to
Am 04.02.2011 14:25, schrieb Stefan Froehlich:
> On Tue, 01 Feb 2011 05:22:44 Niels Braczek wrote:
>> ArrayAccess ist bei Collections sehr nützlich bzw. bequem; es
>> erlaubt auf die Elemente über einen Index mit Array_Notation
>> zuzugreifen.
>
>> $bmw = $carCollection[$id];
>
> Huebsch. (Noch huebscher waere es, wenn auch Dinge wie count() damit
> funktionieren wuerden, aber wahrscheinlich bin ich unbescheiden).

Dann implementiere doch count() als Property/Methode der Collection,
wenn du schon dabei bist, Arrays in Collections zu wrappen:

class CarCollection implements ArrayAccess {
private $_array;

public function count() {
return count($this->_array);
}
}

> Zum Thema Collection: bis dato gibt es das schon diskutierte
> Interface ISortable, mit dem die Objekte selbst festlegen koennen,
> wo und wie sie einsortiert werden wollen. Skizziert in etwa so:
>
> | abstract class A implements ISortable {
> | abstract public function SortValue();
> | }
> | class A1 extends A {
> | public function SortValue() { return $this->propA; }
> | }
> | class A2 extends A {
> | public function SortValue() { return $this->propB; }
> | }
> |
> | $a = array();
> | $a[] = new A1();
> | $a[] = new A2();
> | sort($a);

Wie Niels schon geschrieben hat: So macht das irgendwie keinen Sinn,
weil A1::$propA u. U. nicht mit A2::$propB vergleichbar ist. Du
benötigst schon irgendwie einen gemeinsamen Nenner, der in A enthalten
sein muss.

Was du so erreichen kannst, ist dann eine "Default"-Sortierung. Da ich
als zweite Technologie .NET verwende und die dortigen Konzepte
größtenteils für ziemlich durchdacht befinde, hab ich mir da einiges
abgeschaut. Ich würd eine Standardsortierung deswegen ungefähr so
implementieren:

interface IComparable {
function compareTo(IComparable $other);
}

abstract class A implements IComparable {

/**
* Member, enthält einen string
*/
private $_stringField;

/**
* Beispiel einer konkreten compareTo()-Implementierung
*/
final public function compareTo(IComparable $other) {
// Exception werfen, wenn $other nicht auch ein A ist,
// da fehlen bei PHP Generics ;)
if (!($other instanceof A))
throw new ...

return strcoll($this->_stringField, $other->_stringField);
}
}

class A1 extends A { }

class A2 extends A { }

/**
* Allgemeine Comparer-Klasse für IComparables
*/
final class ComparableComparer {
public static function compare(IComparable $a, IComparable $b) {
return $a->compareTo($b);
}
}

abstract class Collection {
private $_array;

/**
* Stellt sicher, dass nur IComparables in der Collection landen
* können.
*
* Genaugenommen gehört hier noch eine SortableCollection
* eingeschoben: Nicht jede Collection muss sortierbar sein...
*/
final protected function _addItem(IComparable $item) {
$this->_array[] = $item;
}

public function sort() {
usort($this->_array, array('ComparableComparer', 'compare'));
}
}

class CollectionA extends Collection {
public function addItem(A $item) {

$this->_addItem($item);
}
}

> Die Idee der Collections war nun, die Information ueber
> Sortierschluessel aus der Klasse zu entfernen, weil sie dort von der
> Logik her schlecht aufgehoben ist. Bei einfachen Klassen ist das ja
> auch ganz nett.

Ja, find ich auch. Bei Listen von "einfachen" Objekten macht es IMHO
durchaus Sinn, so etwas wie oben zu implementieren.

> Bei Konstrukten wie diesen wird daraus nun aber:
>
> | class CollectionA extends Collection {
> | public function addItem(A $item) { }
> | public function sort() { }
> | }
> |
> | abstract class A {}
> | class A1 extends A {}
> | class A2 extends A {}
> |
> | $a = new CollectionA();
> | $a->addItem(new A1());
> | $a->addItem(new A2());
> | $a->sort();
>
> Es erscheint mir ein wenig ungeschickt, alle Sortierverfahren in
> CollectionA{} abzulegen - dann habe ich sie zwar aus der jeweiligen
> Klassendefinition herausgeloest, dafuer aber klassenuebergreifend in
> einer Datei zusammengefasst; in meinen Augen ist das noch schlimmer,
> als vorher.

Tjo, du brauchst im Prinzip eine dritte Klasse: den Comparer. Ich bau da
quasi immer eine "Dreiheit":

Das Objekt, die Liste von Objekten und den Comparer für das Objekt:

Der ComparerA hätte dann lauter Methoden, die usort() als Callbacks
verwenden kann und zusätzlich ein Feld, dass die Richtung beihaltet:

class ComparerA {
const ASC = 1;
const DESC = -1;

private $_direction;

public function __construct($direction = self::ASC) {
$this->setDirection($direction);
}

public function setDirection($direction) {
$this->_direction = $direction;
}

public function comparePropertyA(A $a, A $b) {}
public function comparePropertyB(A $a, A $b) {}
public function comparePropertyC(A $a, A $b) {}
}

Die obige Collection::sort()-Methode könntest du dann
erweitern/überschreiben:

class CollectionA extends Collection {

public function sort(
ComparerA $comparer = null,
$property = null) {
if (!is_null($comparer)...)
usort($this->_array, array($comparer, 'compareBy'.$property));
else
parent::sort();
}
}

(Alles quick'n'dirty...)

> CollectionA in Subklassen aufzuteilen hat aber
> andererseits auch keinen Sinn, weil der Witz ja gerade darin
> besteht, die unterschiedlichen Objekte in _einer_ Collection
> zusammenfassen zu koennen.

Ja schon. Aber völlig unterschiedliche Objekte in einer Collection
zusammenzufassen, macht selten Sinn. Und diese dann auch sortieren zu
wollen noch weniger... Bzw., wenn du eine CollectionA hast, die Objekte
vom Typ A enthält, kannst du diese auch nur nach "Dingen" sortieren, die
der Typ A enthält. Die CollectionA und der ComparerA wissen ja beide
nicht, welche Subtypen von A es gibt.

Gruß, Helmut

Stefan Froehlich

unread,
Feb 15, 2011, 5:59:09 PM2/15/11
to
On Fri, 04 Feb 2011 18:26:40 Helmut Chang wrote:
> >> ArrayAccess ist bei Collections sehr nützlich bzw. bequem; [...]

> > Huebsch. (Noch huebscher waere es, wenn auch Dinge wie count() damit
> > funktionieren wuerden, aber wahrscheinlich bin ich unbescheiden).

> Dann implementiere doch count() als Property/Methode der Collection

Merci (es gibt immer noch irgendetwas, was mir bislang nicht
untergekommen ist). Wobei "Dinge wie" natuerlich gerne
weiterreichend sein koennte, array_keys() faellt mir da z.B. ein.
Aber immerhin, count() reduziert die notwendigen Aenderungen schon
einmal ganz betraechtlich.

> > | abstract class A implements ISortable {
> > | abstract public function SortValue();
> > | }
> > | class A1 extends A {
> > | public function SortValue() { return $this->propA; }
> > | }
> > | class A2 extends A {
> > | public function SortValue() { return $this->propB; }
> > | }
> > |
> > | $a = array();
> > | $a[] = new A1();
> > | $a[] = new A2();
> > | sort($a);

> Wie Niels schon geschrieben hat: So macht das irgendwie keinen
> Sinn, weil A1::$propA u. U. nicht mit A2::$propB vergleichbar ist.
> Du benötigst schon irgendwie einen gemeinsamen Nenner, der in A
> enthalten sein muss.

Das sagt sich immer so leicht. Um die Relationen klarzustellen: von
den ueber 100 Collections, die es inzwischen gibt, taucht bei gerade
einmal zwei die Fragestellung auf. Und ja, da werden tatsaechlich
Aepfel mit Birnen verglichen - weil die Anwender gefunden haben, es
waere eine gute Idee, die Dinge in eine gemeinsame Auswahlliste zu
verpacken.

Nun koennte ich natuerlich eigens fuer diese Selectbox noch einmal
eine eigene Klasse definieren, die die beiden anderen in sich
vereint, aber das scheint auch ein wenig uebertrieben... Ich lasse
es in dem Fall halt einfach bei den klassenspezifischen Methoden
(was letztlich irgendwo der "gemeinsame Nenner" ist, bloss halt
nicht im Rahmen des Objektschemas).



> Tjo, du brauchst im Prinzip eine dritte Klasse: den Comparer. Ich
> bau da quasi immer eine "Dreiheit":

> Das Objekt, die Liste von Objekten und den Comparer für das Objekt:

> Der ComparerA hätte dann lauter Methoden, die usort() als Callbacks
> verwenden kann und zusätzlich ein Feld, dass die Richtung beihaltet:

Hm. Im Prinzip kann man den Comparer aber doch auch immer in der
(sortierbaren) Liste aufgehen lassen, also als "extends Collection"
formulieren: was sortierbar ist, kann zu Listen kumuliert werden,
und jede sortierbare Liste benoetigt eine Sortierfunktion.



> völlig unterschiedliche Objekte in einer Collection
> zusammenzufassen, macht selten Sinn. Und diese dann auch sortieren
> zu wollen noch weniger...

Was willst Du machen? Du hast Waehrungen, Du hast
Verpackungseinheiten - die haben genau gar nichts miteinander zu
tun. Und irgendwann kommt jemand und moechte die beiden in _einer_
Auswahlbox. Abhaengig davon, was gewaehlt wird, geht der weitere
Verlauf dann wieder in gaenzlich andere Richtungen...

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

So ein hinterlistiger Gedanke: Stefan!
(Sloganizer)

Stefan Froehlich

unread,
Feb 15, 2011, 6:11:09 PM2/15/11
to
On Sun, 30 Jan 2011 04:01:27 Niels Braczek wrote:
> > Eleganter waere es, zugegeben.

> Und einfacher in Entwurf, Pflege und Erweiterung.

Schauen wir einmal - zwei Fragen stellen sich mir ad hoc. Das erste
Problem tritt bei Arrays ganz genauso auf, aber eventuell bietet das
Objektmodell ja neue Moeglichkeiten:

Zwei Methoden iterieren mit foreach() ueber das gleiche array bzw.
die gleiche Collection. Ruft nun die eine Methode - auch ueber
Umwege - die andere Methode auf, gibt es ein ernsthaftes Problem,
weil foreach() den Arraypointer veraendert => eine Endlosschleife
ist in der Regel das Ergebnis. Kann man das nicht irgendwie umgehen?

Das zweite Problem ist bislang nur theoretischer Natur, koennte aber
schon auch ein Thema werden - wieder einmal Collections in einem
hierarchischen Objektmodell. Grob skizziert am Beispiel eines Obst-
und Gemuesehaendlers (man verzeihe mir allfaellige Tippfehler):

| abstract class Collection { }
|
| abstract class Vegetable {
| public function display(VegetableCollection $items) {
| foreach ($items as $item) {
| echo $item->Name() . "\n";
| }
| }
|
| public function addGoodie(VegetableCollection $items, Vegetable $goodie) {
| $items->addItem($goodie);
| }
| }
| class VegetableCollection extends Collection { }
|
| class Bean extends Vegetable {
| public static function getInstances() { return new BeanCollection('give_me_all_beans'); }
| }
| class BeanCollection extends VegetableCollection { }
|
| class Tomato extends Vegetable { }
| class TomatoCollection extends VegetableCollection { }
|
| $myBasket = Beans::getInstances();
| Vegetables::display($myBasket);
|
| $tomato = new Tomato();
| $myBasket->addGoodie($tomato);

Es ist in meinen Augen legitim, dass die abgeleiteten Klassen
Collections ihres Typs zurueckgeben. Wenn ich Bohnen anfordere,
moechte ich auch (garantiert) Bohnen bekommen, weil ich eventuell
Dinge damit mache, die nur mit Bohnen getan werden koennen.

Auf der anderen Seite ist es in meinen Augen legitim, in der
Oberklasse zu erwarten, dass ich mit den von meinen Methoden
geforderten, generischen Collections auch generische Dinge getan
werden koennen: wenn mir jemand einen Korb Gemuese gibt, moechte
ich ein weiteres Stueck Gemuese hinzufuegen koennen.

Wie loest man (sinnvoll und elegant) das Dilemma, dass
BeanCollection zwar an Stelle von VegetableCollection akzeptiert
wird und fuer lesende Zugriffe auch brauchbar ist, fuer schreibende
Zugriffe aber erst nach VegetableCollection konvertiert werden
muesste?

Oder, anders formuliert: wie gestalte ich meine Collections fuer
eine Objekthierarchie _jetzt_ am besten, so dass mir das nicht
spaeter entweder in die eine (zu spezialisiert, allgemeine Zugriffe
schlagen fehl) oder in die andere (zu allgemein, spezielle Methoden
sind nicht verwendbar) Richtung auf den Kopf faellt?

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan - kompetente Liebe, die nimmerdar flucht.
(Sloganizer)

Niels Braczek

unread,
Feb 15, 2011, 11:46:21 PM2/15/11
to
Stefan Froehlich schrieb:

> Zwei Methoden iterieren mit foreach() ueber das gleiche array bzw.
> die gleiche Collection. Ruft nun die eine Methode - auch ueber
> Umwege - die andere Methode auf, gibt es ein ernsthaftes Problem,
> weil foreach() den Arraypointer veraendert => eine Endlosschleife
> ist in der Regel das Ergebnis. Kann man das nicht irgendwie umgehen?

Dann verwendet man getrennte Iteratoren. Ein Iterator *arbeitet* auf
einer Collection, ist aber nicht Teil davon. Die Collection bietet
jedoch mit getIterator() eine standardisierte Methode, sich einen zur
Collection passenden Iterator zu besorgen.

> Das zweite Problem ist bislang nur theoretischer Natur, koennte aber
> schon auch ein Thema werden - wieder einmal Collections in einem
> hierarchischen Objektmodell. Grob skizziert am Beispiel eines Obst-
> und Gemuesehaendlers (man verzeihe mir allfaellige Tippfehler):
>
> | abstract class Collection { }
> |
> | abstract class Vegetable {
> | public function display(VegetableCollection $items) {

^^^^^^^^^^^^^^^^^^^
Die Items wissen nichts über übergeordnete Strukturen. Sie kennen keine
Collections.

> | foreach ($items as $item) {
> | echo $item->Name() . "\n";

^^^^
Die Items sollten auch nichts darüber wissen, in welcher Umgebung sie
verwendet werden. Die Gestaltung des Zeilenumruchs ist Aufgabe und
Hoheit des Renderers (zB. als Visitor implementiert). Für Demo-Zwecke
ist das aber ok.

> | }
> | }
> |
> | public function addGoodie(VegetableCollection $items, Vegetable $goodie) {
> | $items->addItem($goodie);
> | }

Diese Methode gehört ebenfalls eindeutig in die Collection.

> | }
> | class VegetableCollection extends Collection { }
> |
> | class Bean extends Vegetable {
> | public static function getInstances() { return new BeanCollection('give_me_all_beans'); }

Wenn so etwas überhaupt Sinn macht, gehört das in BeanCollection.
getInstance() suggeriert ein Singleton, das hier aber nicht gegeben ist.

> | }
> | class BeanCollection extends VegetableCollection { }
> |
> | class Tomato extends Vegetable { }
> | class TomatoCollection extends VegetableCollection { }
> |
> | $myBasket = Beans::getInstances();
> | Vegetables::display($myBasket);
> |
> | $tomato = new Tomato();
> | $myBasket->addGoodie($tomato);

Wenn du Basket als VegetableCollection beutzen willst, muss es auch eine
sein.

> Es ist in meinen Augen legitim, dass die abgeleiteten Klassen
> Collections ihres Typs zurueckgeben. Wenn ich Bohnen anfordere,
> moechte ich auch (garantiert) Bohnen bekommen, weil ich eventuell
> Dinge damit mache, die nur mit Bohnen getan werden koennen.


Ich lasse abstract/interface der Einfachheit mal weg, da dies eher als
Skizze ins Unreine geschrieben ist:

class Collection implements IteratorAggregate


{
private $items = array();
public function addItem( $item )
{

$this->items[] = $item;
}
public function append( Collection $collection )
{
$this->items+= $collection;
}
public function display() {}
public function getIterator() {
return new ArrayIterator( $this->items );
}
...
}

class Vegetable
{
...
public function display()
{
echo $this->getName();
}
...
}
class VegetableCollection extends Collection
{
...
public function addItem( Vegetable $item )
{
parent::addItem( $item );
}
public function append( VegetableCollection $collection )
{
parent::append( $collection );
}
public static function filter( VegetableCollection $collection )
{
$subset = new self(); // Hier bin ich nicht sicher, ob das so
// funktioniert - ggf. muss die Methode
// in abgeleitete Klassen kopiert werden.
foreach ( $collection as $item ) {
try {
$subset->addItem( $item );
} catch ( Exception $e ) {}
}
return $subset;
}
public function display()
{
foreach ( $this as $item ) {
$item->display();
}
}
...
}

class Bean extends Vegetable {}
class BeanCollection extends VegetableCollection
{
...
public function addItem( Bean $item )
{
parent::addItem( $item );
}
public function append( BeanCollection $collection )
{
parent::append( $collection );
}
public static function getAllBeans()
{


return new BeanCollection( 'give_me_all_beans' );
}

...
}

class Tomato extends Vegetable {}
class TomatoCollection extends VegetableCollection {}

$myBasket = new VegetableCollection();
$myBasket->append( BeanCollection::getAllBeans() );
$myBasket->display(); // Nur Bohnen

$tomato = new Tomato();
$myBasket->addItem( $tomato );
$myBasket->display(); // Bohnen mit einer Tomate

$beans = BeanCollection::filter( $myBasket );
$beans->display(); // Keine Tomate mehr!

> Auf der anderen Seite ist es in meinen Augen legitim, in der
> Oberklasse zu erwarten, dass ich mit den von meinen Methoden
> geforderten, generischen Collections auch generische Dinge getan
> werden koennen: wenn mir jemand einen Korb Gemuese gibt, moechte
> ich ein weiteres Stueck Gemuese hinzufuegen koennen.

Wenn es aber ein Bohnenkorb ist, gehören da keine Tomaten rein. Genau
davor sollen die (Klassen)Typen ja sorgen. Deshalb verwendest du dort,
wo die generische Collection brauchst, eben die generische Collection
und keine andere. Wenn du erst ein Tomate bei den Bohnen hast, kannst du
die Gesamtheit eben nicht mehr behandeln, als bestünde sie nur aus
Bohnen. Dazu musst du die Bohnen erst isolieren. Das kann wie oben
angedeutet über einen Filter geschehen oder über einen speziellen Iterator.

> Wie loest man (sinnvoll und elegant) das Dilemma, dass
> BeanCollection zwar an Stelle von VegetableCollection akzeptiert
> wird und fuer lesende Zugriffe auch brauchbar ist, fuer schreibende
> Zugriffe aber erst nach VegetableCollection konvertiert werden
> muesste?

Gar nicht. Das Problem existiert nicht.

> Oder, anders formuliert: wie gestalte ich meine Collections fuer
> eine Objekthierarchie _jetzt_ am besten, so dass mir das nicht
> spaeter entweder in die eine (zu spezialisiert, allgemeine Zugriffe
> schlagen fehl) oder in die andere (zu allgemein, spezielle Methoden
> sind nicht verwendbar) Richtung auf den Kopf faellt?

S.o.: Indem du das Pferd von der anderen Seite her aufzäumst und immer
genau die Klasse verwendest, die dem Anwendungsfall angemessen ist.

Stefan Froehlich

unread,
Feb 17, 2011, 11:10:53 AM2/17/11
to
On Wed, 16 Feb 2011 05:46:21 Niels Braczek wrote:
> > Zwei Methoden iterieren mit foreach() ueber das gleiche array bzw.
> > die gleiche Collection. [...]

> Dann verwendet man getrennte Iteratoren. Ein Iterator *arbeitet* auf
> einer Collection, ist aber nicht Teil davon. Die Collection bietet
> jedoch mit getIterator() eine standardisierte Methode, sich einen zur
> Collection passenden Iterator zu besorgen.

Das hiesse aber, auf das elegante:

| abstract class Collection implements Iterator

zu verzichten (wenigstens dann in der Anwendung), was ja nicht Sinn der
Sache ist; so, wie ich das umgesetzt habe, fallen Collections und
Iteratoren zusammen. Ich bin aber irgendwann draufgekommen, dass das ja
eigentlich gar nicht vorkommen kann/darf, da die Collection in Schleifen
nirgendwo hineingereicht wird. Konkulusio: Programmierfehler, es fehlte ein
clone() an entscheidender Stelle. Normalerweise merke ich solche Dinge ja
beim Schreiben des ausloesenden Postings, aber irgendwie...



> > Das zweite Problem ist bislang nur theoretischer Natur, koennte aber
> > schon auch ein Thema werden - wieder einmal Collections in einem
> > hierarchischen Objektmodell. Grob skizziert am Beispiel eines Obst-
> > und Gemuesehaendlers (man verzeihe mir allfaellige Tippfehler):

> > | abstract class Collection { }
> > |
> > | abstract class Vegetable {
> > | public function display(VegetableCollection $items) {
^^^^^^^^^^^^^^^^^^^
> Die Items wissen nichts über übergeordnete Strukturen. Sie kennen keine
> Collections.

$items hier doch lediglich der Name fuer einen Haufen aus nicht naeher
definiertem Gemuese.



> > | foreach ($items as $item) {
> > | echo $item->Name() . "\n";
> ^^^^
> Die Items sollten auch nichts darüber wissen, in welcher Umgebung sie
> verwendet werden. Die Gestaltung des Zeilenumruchs ist Aufgabe und
> Hoheit des Renderers (zB. als Visitor implementiert). Für Demo-Zwecke
> ist das aber ok.

Das \n ist ob des Abstraktionsgrades des Beispiels sowieso voellig
ueberfluessig, ja. Ich brauchte bloss irgendeine exemplarische
Methode, die mit dem Haufen Gemuese etwas tut. "Anzeigen" ist mir da
halt als erstes eingefallen, aber Du kannst gerne statt dessen auch
"wiegen" oder "Naehrwert bestimmen" verwenden, das aendert nichts an
der Frage als solcher.

> > | public function addGoodie(VegetableCollection $items, Vegetable $goodie) {
> > | $items->addItem($goodie);
> > | }

> Diese Methode gehört ebenfalls eindeutig in die Collection.

Ja, stimmt. Auch das aendert allerdings nichts am Kern der Frage.



> > | class VegetableCollection extends Collection { }
> > |
> > | class Bean extends Vegetable {
> > | public static function getInstances() { return new BeanCollection('give_me_all_beans'); }
>
> Wenn so etwas überhaupt Sinn macht, gehört das in BeanCollection.
> getInstance() suggeriert ein Singleton, das hier aber nicht gegeben ist.

Ich gebe zu, ich hatte vermutlich eine Singleton-Klasse im
Hinterkopf, was fuer Bohnen natuerlich relativ schwachsinnig ist.
Auch hier: Sinn der Uebung ist es, unter irgendeinem Vorwand eine
Collection mit Dingen der Unterklasse zu bekommen, hier also z.B.
einen Haufen Bohnen oder einen Haufen Tomaten.

(Zusatzfrage: weshalb nach BeanCollection? Das Objekt, welches
zurueckgegeben wird, ist selbstverstaendlich eine BeanCollection,
aber in dieser Methode geht es ja eher darum, die einzelnen Bohnen
auszuwaehlen, was in die Domaene von Bean{} faellt. Dass die
Selektion bei "getAll" recht einfach ist, tut dem IMHO keinen
Abbruch)

> > | $myBasket = Beans::getInstances();
> > | Vegetables::display($myBasket);
> > |
> > | $tomato = new Tomato();
> > | $myBasket->addGoodie($tomato);

Na bitte, ich habe die Methode ja eh gedanklich in der Collection
gehabt ;-)



> Wenn du Basket als VegetableCollection beutzen willst, muss es
> auch eine sein.

Aber das ist doch genau das Problem: wenn ich beim Gemuesehaendler
"eine Steige Erdbeeren" verlange, dann hat der keinen Anlass, etwas
generischeres zurueckzugeben, als eine Steige Erdbeeren. Fuege ich
zwei Stunden spaeter noch ein paar Himbeere dazu, aendert sich der
Typ dessen, was ich in der Hand habe, auf etwas generischeres.

Diese Logik klappt beim Programmieren mit gewoehnlichen Objekten
noch ganz gut: uebergebe ich ein Objekt vom Typ Bean an eine
Funktion, die Vegetable erwartet, kann ich in dieser Funktion guten
Gewissens all das tun, was mir Vegetable zu tun erlaubt.

Mit Collections geht das nicht: kommt ein Sack Bohnen hinein, darf
hinterher nur genau das getan werden, was ein Sack Bohnen erlaubt.
Ich muss also bereits beim Erstellen der Collection wissen, was
irgendwann spaeter damit geschehen soll - und das finde ich (der
Vergleich mit den Beeren ist gar nicht so verkehrt) gerade ein wenig
unpraktisch.



> Ich lasse abstract/interface der Einfachheit mal weg, da dies eher als
> Skizze ins Unreine geschrieben ist:

[...]

> $myBasket->append( BeanCollection::getAllBeans() );

[...]

Wenn ich dazu aus meinem vorigen Posting zitieren darf:

| dass [...] fuer schreibende Zugriffe aber erst nach
| VegetableCollection konvertiert werden muesste?

Genau das tust Du ja hiermit (was letztendlich wohl, wenn auch
unbewusst, genau die gewuenschte Antwort auf meine Frage ist, welche
Variante weniger Schmerzen verursacht).

Solange das alles in der gleichen Programmebene stattfindet, ist das
ja auch huebsch und einfach:

> $myBasket = new VegetableCollection();
> $myBasket->append( BeanCollection::getAllBeans() );

> $tomato = new Tomato();
> $myBasket->addItem( $tomato );

Sobald man aber eine Funktion hat, die nach einer
VegetableCollection verlangt, darf man der im Prinzip nur noch eine
VegetableCollection uebergeben, aber keine davon abgeleitete,
spezialisierte Klasse mehr, weil man ja nie dafuer garantieren
kann, was innerhalb der Funktion damit weiter passiert. I.e. das in
meinen Augen wesentlich naheliegendere:

| function doSomething(VegetableCollection $collection) { }
| $result = doSomething(BeanCollection::getAllBeans());

...muss durch ein:

| $myBasket = new VegetableCollection();
| $myBasket->append( BeanCollection::getAllBeans() );

| $result = doSomething($myBasket);

abgeloest werden. Dann sollte man sich das:

| class BeanCollection extends VegetableCollection

...aber vermutlich besser gleich ganz sparen. Wenigstens kann ich
keinen Gewinn dahinter erkennen, sondern nur moegliche Fehlerquellen.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Mies? Mieser als Stefan!? Ob's wahr ist?!
(Sloganizer)

Niels Braczek

unread,
Feb 17, 2011, 11:46:28 PM2/17/11
to
Stefan Froehlich schrieb:

> On Wed, 16 Feb 2011 05:46:21 Niels Braczek wrote:

>> > | abstract class Collection { }
>> > |
>> > | abstract class Vegetable {
>> > | public function display(VegetableCollection $items) {
> ^^^^^^^^^^^^^^^^^^^
>> Die Items wissen nichts über übergeordnete Strukturen. Sie kennen keine
>> Collections.
>
> $items hier doch lediglich der Name fuer einen Haufen aus nicht naeher
> definiertem Gemuese.

Dennoch hat das einzelne Item nichts über Eigenschaften einer Collection
zu wissen.

>> Wenn so etwas überhaupt Sinn macht, gehört das in BeanCollection.
>> getInstance() suggeriert ein Singleton, das hier aber nicht gegeben ist.
>
> Ich gebe zu, ich hatte vermutlich eine Singleton-Klasse im
> Hinterkopf, was fuer Bohnen natuerlich relativ schwachsinnig ist.
> Auch hier: Sinn der Uebung ist es, unter irgendeinem Vorwand eine
> Collection mit Dingen der Unterklasse zu bekommen, hier also z.B.
> einen Haufen Bohnen oder einen Haufen Tomaten.

Ok, aber gerade die Überlegungen, welche Klasse für was zuständig ist,
sind der wesentliche Kern guten Designs. Einer Bohne eine Tüte Bohnen zu
entnehmen mach definitiv keinen Sinn.

> (Zusatzfrage: weshalb nach BeanCollection? Das Objekt, welches
> zurueckgegeben wird, ist selbstverstaendlich eine BeanCollection,

Da eine einzelne Bohne nichts von Bohnenansammlungen weiß (wissen darf),
*kann* die Methode nicht in Bean untergebracht sein.

> aber in dieser Methode geht es ja eher darum, die einzelnen Bohnen
> auszuwaehlen, was in die Domaene von Bean{} faellt.

Nein. Eine Bohne ist *eine* Bohne.

>> Wenn du Basket als VegetableCollection beutzen willst, muss es
>> auch eine sein.
>
> Aber das ist doch genau das Problem: wenn ich beim Gemuesehaendler
> "eine Steige Erdbeeren" verlange, dann hat der keinen Anlass, etwas
> generischeres zurueckzugeben, als eine Steige Erdbeeren. Fuege ich
> zwei Stunden spaeter noch ein paar Himbeere dazu, aendert sich der
> Typ dessen, was ich in der Hand habe, auf etwas generischeres.

Eben. Du kannst aus dem Inhalt der Steige keinen Erdbeer-Shake mehr
machen, ohne die Beeren wieder auseinander zu sortieren. Man kann
sicherlich die automagische Konvertierung von StrawberryCollection nach
BerryCollection (also den Verlust der für Erdbeeren spezifischen
Möglichkeiten) simulieren (Ansatz: Zustandsmuster), aber die
Konvertierung ist zwingend erforderlich, wenn die
StrawberryCollection-Klasse nötig ist. Meistens ist sie es eher nicht.

Tatsächlich liefert dir der Händler zwar eine StrawberryCollection, aber
du weißt, dass du noch Himbeeren dazutun willst, also machst du daraus
bereits bei der Übernahme den generischeren Typ.

> Diese Logik klappt beim Programmieren mit gewoehnlichen Objekten
> noch ganz gut: uebergebe ich ein Objekt vom Typ Bean an eine
> Funktion, die Vegetable erwartet, kann ich in dieser Funktion guten
> Gewissens all das tun, was mir Vegetable zu tun erlaubt.
>
> Mit Collections geht das nicht: kommt ein Sack Bohnen hinein, darf
> hinterher nur genau das getan werden, was ein Sack Bohnen erlaubt.
> Ich muss also bereits beim Erstellen der Collection wissen, was
> irgendwann spaeter damit geschehen soll - und das finde ich (der
> Vergleich mit den Beeren ist gar nicht so verkehrt) gerade ein wenig
> unpraktisch.

Mit einer Collection kannst du tun, was man mit einer *Collection* tun
kann: Sortieren, Items hinzufügen oder löschen, suchen, ... Was die
Items als Einzelobjekt können, ist dabei völlig unerheblich.

>> Ich lasse abstract/interface der Einfachheit mal weg, da dies eher als
>> Skizze ins Unreine geschrieben ist:
>
> [...]
>> $myBasket->append( BeanCollection::getAllBeans() );
> [...]
>
> Wenn ich dazu aus meinem vorigen Posting zitieren darf:
>
> | dass [...] fuer schreibende Zugriffe aber erst nach
> | VegetableCollection konvertiert werden muesste?
>
> Genau das tust Du ja hiermit (was letztendlich wohl, wenn auch
> unbewusst, genau die gewuenschte Antwort auf meine Frage ist, welche
> Variante weniger Schmerzen verursacht).

Da wird nicht konvertiert; die Bohnen werden nur in die Gemüsesammlung
aufgenommen. $myBasket ist von Anfang an eine VegetableCollection.

> Solange das alles in der gleichen Programmebene stattfindet, ist das
> ja auch huebsch und einfach:
>
>> $myBasket = new VegetableCollection();
>> $myBasket->append( BeanCollection::getAllBeans() );
>> $tomato = new Tomato();
>> $myBasket->addItem( $tomato );
>
> Sobald man aber eine Funktion hat, die nach einer
> VegetableCollection verlangt, darf man der im Prinzip nur noch eine
> VegetableCollection uebergeben, aber keine davon abgeleitete,
> spezialisierte Klasse mehr, weil man ja nie dafuer garantieren
> kann, was innerhalb der Funktion damit weiter passiert.

Doch, natürlich. Wenn die Funktion eine VegetableCollection erwartet,
wird sie auch bei einer BeanCollection alles vorfinden, was sie braucht.

> I.e. das in
> meinen Augen wesentlich naheliegendere:
>
> | function doSomething(VegetableCollection $collection) { }
> | $result = doSomething(BeanCollection::getAllBeans());
>

> ....muss durch ein:


>
> | $myBasket = new VegetableCollection();
> | $myBasket->append( BeanCollection::getAllBeans() );
> | $result = doSomething($myBasket);
>
> abgeloest werden.

Nein, warum? doSomething() kann prima mit einer BeanCollection umgehen,
da diese eine VegetableCollection ist. Wenn doSomething() etwas mit
Bohnen vorhat, muss die Signatur eine solche verlangen.

> Dann sollte man sich das:
>
> | class BeanCollection extends VegetableCollection
>

> ....aber vermutlich besser gleich ganz sparen. Wenigstens kann ich


> keinen Gewinn dahinter erkennen, sondern nur moegliche Fehlerquellen.

Die Lösung liegt woanders. Ich lese zwischen den Zeilen, dass du in
doSomething() verschiedene Gemüsesorten unterschiedlich behandeln
willst. In dem Fall gehört der sortenspezifische Code wieder in die
Collection, während doSomething() nur den Algorithmus festlegt
(Template-Pattern). Statt also

function doSomething( VegetableCollection $collection )
{
... // do something common
if ( $collection instanceOf BeanCollection ) {
// do something with beans
} else {
// do something else
}
... // do something common
}

benutzt du

class VegetableCollection
{
...
public function doSomething()
{
// do something else
}
...
}

class BeanCollection
{
...
public function doSomething()
{
// do something with beans
}
...
}

function doSomething( VegetableCollection $collection )
{
... // do something common
$collection->doSomething();
... // do something common
}

Somit hast du alles dort, wo es hingehört.

Stefan Froehlich

unread,
Mar 10, 2011, 11:20:17 AM3/10/11
to
So, jetzt muss ich mich da endlich einmal dransetzen, bevor mir die
Gruppe und damit der bisherige Thread unter dem Hintern weggezogen
werden (wegen Gruppenumzug X'post und F'up nach dclp).

On Fri, 18 Feb 2011 05:46:28 Niels Braczek wrote:

[public static function BeanCollection::getAllInstances()]


> > (Zusatzfrage: weshalb nach BeanCollection? Das Objekt, welches
> > zurueckgegeben wird, ist selbstverstaendlich eine BeanCollection,

> Da eine einzelne Bohne nichts von Bohnenansammlungen weiß (wissen
> darf), *kann* die Methode nicht in Bean untergebracht sein.

Doch, kann sie durchaus. Die statische Methode gehoert ja zu gar
keinem Objekt, weder aus Bean noch aus BeanCollection, sie gibt
lediglich ein Objekt eines bestimmten Typs zurueck, hier eine
BeanCollection. Aber eine Methode, die einen String als Ergebnis
liefert, werde ich deshalb auch nicht gleich in eine String-Klasse
verlegen.

X::getInstances() muss sowohl mit den Einzelobjekten umgehen (und
ueblicherweise eine Menge ueber deren Eigenschaften wissen), als
auch mit deren Collections (davon muss aber nur bekannt sein, wie
man sie anlegt und befuellt). Lege ich die Methode zur Collection,
habe ich bei Aenderungen der Klasse (mehr) Wartungsaufwand an zwei
verschiedenen Stellen. Das Interface der Collection sollte damit
verglichen eher langzeitstabil sein.


> > das ist doch genau das Problem: wenn ich beim Gemuesehaendler
> > "eine Steige Erdbeeren" verlange, dann hat der keinen Anlass,
> > etwas generischeres zurueckzugeben, als eine Steige Erdbeeren.
> > Fuege ich zwei Stunden spaeter noch ein paar Himbeere dazu,
> > aendert sich der Typ dessen, was ich in der Hand habe, auf etwas
> > generischeres.

> Eben. Du kannst aus dem Inhalt der Steige keinen Erdbeer-Shake
> mehr machen, ohne die Beeren wieder auseinander zu sortieren.

Ja, klar.

Wenn ich aber beschliesse, einen Beeren-Mix einzukaufen, dann werde
ich beim fliegenden Haendler mit den Erdbeeren genau das gleiche
verlangen, wie fuer ein Erdbeer-Shake: naemlich eine Steige
Erdbeeren. Diese Steige kann ich dann sowohl in das eine, als auch
in das andere Rezept kippen - durch die Vererbung ist es fuer beide
brauchbar.

Was mich nun stoert ist, dass ich zwei unterschiedliche Dinge
verlangen muss, obwohl ich (aus Sicht des Liefernden) genau das
gleiche haben moechte.

> Man kann sicherlich die automagische Konvertierung von
> StrawberryCollection nach BerryCollection (also den Verlust der
> für Erdbeeren spezifischen Möglichkeiten) simulieren (Ansatz:
> Zustandsmuster)

Ja, das waere in etwa das, was mir vorschwebt, wobei ich da
(teilweise auch einfach mangels Zeit) noch keinen brauchbaren Weg
gefunden habe. Dass zwischen den beiden Typen unterschieden werden
muss, ist ja vollkommen logisch, nur die sich dadurch ergebenden
Reibungspunkte haette ich gerne weg.

> >> $myBasket->append( BeanCollection::getAllBeans() );

> > Genau das [konvertieren] tust Du ja hiermit (was letztendlich


> > wohl, wenn auch unbewusst, genau die gewuenschte Antwort auf
> > meine Frage ist, welche Variante weniger Schmerzen verursacht).

> Da wird nicht konvertiert; die Bohnen werden nur in die
> Gemüsesammlung aufgenommen. $myBasket ist von Anfang an eine
> VegetableCollection.

Du hast vorher eine BeanCollection, danach eine VegetableCollection,
das betrachte ich als Konvertierung. Dass das natuerlich zwei
verschiedene Instanzen von Objekten sein muessen, liegt in der
Natur der Programmiersprache.



> > Sobald man aber eine Funktion hat, die nach einer
> > VegetableCollection verlangt, darf man der im Prinzip nur noch
> > eine VegetableCollection uebergeben, aber keine davon
> > abgeleitete, spezialisierte Klasse mehr, weil man ja nie dafuer
> > garantieren kann, was innerhalb der Funktion damit weiter
> > passiert.

> Doch, natürlich. Wenn die Funktion eine VegetableCollection
> erwartet, wird sie auch bei einer BeanCollection alles vorfinden,
> was sie braucht.

| function doSomething(VegetableCollection $myColl) {
| $myColl->addItem(new Bean());
| }
|
| $coll = new TomatoCollection();
| doSomething($coll);

Und nun?



> doSomething() kann prima mit einer BeanCollection umgehen, da
> diese eine VegetableCollection ist. Wenn doSomething() etwas mit
> Bohnen vorhat, muss die Signatur eine solche verlangen.

Es ist vollkommen offensichtlich, dass mein 6-Zeiler nicht
funktioniert. Der Fehler ist aber... also in meinen Augen sollte er
nicht _so_ passieren koennen:

.) Der Aufruf von doSomething() ist korrekt, ich darf abgeleitete
Klassen uebergeben, wenn Elternklassen in der Signatur vorkommen.

.) Die Funktion fuer sich ist auch korrekt: wenn ich mit Objekten
der Elternklasse arbeite(n muss), darf ich natuerlich auch Dinge
tun, die damit getan werden koennen.

Bei einzelnen Objekten endet das alles in Wohlgefallen: jedes Kind
erbt (und erweitert ggf.) die Funktionalitaet seiner Eltern. Bei den
Collections funktioniert das nun aber nicht mehr: die abgeleitete
Klasse bietet prinzipbedingt _weniger_ Moeglichkeiten, als die
uebergeordnete.

Wie gesagt, das Beispiel _kann_ so natuerlich nicht funktionieren.
Es illustriert aber die groessere Unsicherheit, die sich bei der
Verwendung ergibt: das Einhalten der Typdeklaration in der Signatur
allein ist nicht mehr ausreichend fuer (auf Ebene der Sprache)
fehlerfreie Ausfuehrung.

Servus,
Stefan

--
http://kontaktinser.at/ - die kostenlose Kontaktboerse fuer Oesterreich
Offizieller Erstbesucher(TM) von mmeike

Stefan: Vergiß und bereue.
(Sloganizer)

0 new messages