Zugriff über mehrere Instanzen = guter Stil?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Zugriff über mehrere Instanzen = guter Stil?

Beitragvon Michael Schneider » Mittwoch 2. Juli 2008, 09:54

Hallo!

Da ich kein Informatiker bin (d.h. ich es nicht gelernt habe) und es von Programmiersprache zu Programmiersprache unterschiedliche Ansätze gibt, wollte ich mal wieder eine prinzipielle Frage stellen.

Folgende Situation als einfachstes Beispiel:
Es gibt 3 Instanzen A, B und C von selbst definierten Klassen.
Dabei hält A eine Referenz auf B und B eine Referenz auf C

Frage: ist es nun "schlechter Programmierstil", wenn A eine Methode von C direkt aufruft, weil bei guter Programmierung B die Vermittlerrolle spielen und die Nachricht an C delegieren sollte?
Das wäre doch zumindest für spätere Änderungen hilfreich, da man Referenzen über mehrere Instanzen sehr schlecht verfolgen kann.

Ich würde gern wissen, wie das in Pythonkreisen gesehen wird.

Gruß,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Mittwoch 2. Juli 2008, 10:04

Generell gilt, dass man das so weit wie möglich vermeiden sollte, aber ich finde, eine Indirektion ist derweilen vertretbar. Man vergleiche und bilde sich selbst eine Meinung:

Code: Alles auswählen

fleet.ships.append(ship)

vs.

Code: Alles auswählen

fleet.append_ship(ship)

Stefan
Benutzeravatar
Gromit
User
Beiträge: 51
Registriert: Montag 8. Mai 2006, 19:07

Es fängt meist simpel an ...

Beitragvon Gromit » Mittwoch 2. Juli 2008, 10:28

... aber aus,

Code: Alles auswählen

def addShip( self, someShip ):
    self.ships.append( someShip )

welches man noch weg lassen könnte, wird schnell

Code: Alles auswählen

def addShip( self, myNewShip ):
    self.ships.append( myNewShip )
    myNewShip.shipyard = self
    myNewShip.owner = self.owner

etc. pp. und dann ist guter Rat teuer, wenn sich die Zugriffe auf
das Attribut ships häufen und man es änder muß.

HTH
Gerald
lunar

Beitragvon lunar » Mittwoch 2. Juli 2008, 10:53

sma hat geschrieben:Generell gilt, dass man das so weit wie möglich vermeiden sollte, aber ich finde, eine Indirektion ist derweilen vertretbar. Man vergleiche und bilde sich selbst eine Meinung:

Code: Alles auswählen

fleet.ships.append(ship)

vs.

Code: Alles auswählen

fleet.append_ship(ship)

Stefan

Auf der anderen Seite:

Code: Alles auswählen

fleet.ship.captain.name

vs.

Code: Alles auswählen

fleet.ship.name

vs.

Code: Alles auswählen

fleet.name

Imho sollte man da nicht zu viel mit irgendwelchen Designprinzipien rangehen, sondern einfach mit gesunden Menschenverstand. Bei sma's Beispiel sollte die Indirektion vermieden werden, weil sie unnötig ist: Eine Flotte besteht aus Schiffen, man braucht kein extra Wrapper-Objekt.

Ob es tatsächlich generelle Meinung ist, dass man Indirektionen vermeiden sollte, bezweifele ich mal. Große Frameworks nutzen viel Indirektion, man muss sich nur mal die TextDocument-API von Qt4 anschauen. Ich persönlich finde das nicht schlecht oder "evil", solange es im Rahmen bleibt und vor allem lesbar und logisch ist.
BlackJack

Beitragvon BlackJack » Mittwoch 2. Juli 2008, 11:30

Da nach "pythonisch" gefragt wurde: Ich würde sagen das Gesetz von Demeter wird in der englischsprachigen Newsgroup relativ oft erwähnt und die Bibliotheken sind in der Regel auch nicht so entworfen, dass man viele Punktoperatoren hintereinander anwenden kann. Im Gegensatz zu Sprachen wie Smalltalk oder Io, wo die APIs so einen Stil fördern.

Bei ``fleet.ships.append(ship)`` wird ein wenig wissen über die internen Strukturen vom `fleet`-Exemplar preisgegeben, denn `append()` suggeriert, dass die Schiffe irgendwie linear geordnet verwaltet werden. Das muss ja eigentlich nicht sein. Wenn man irgendwann auf die Idee kommt, dass ein Schiff nicht mehrmals in der Flotte sein kann und `fleet.ships` mit einem `set` realisiert, bekommt man Probleme, wenn Code von aussen direkt auf das `ships`-Attribut zugreift, denn `set.append()` gibt's nicht.

@Gromit: Die Änderung lässt sich aber noch mit Properties umsetzen, ohne das der Client-Code etwas davon merkt.

@lunar: Die Qt4-API lässt sich nicht als Begründung für "pythonisch" oder nicht heranziehen, da es im Kern eine C++-API ist.
lunar

Beitragvon lunar » Mittwoch 2. Juli 2008, 11:52

BlackJack hat geschrieben:@lunar: Die Qt4-API lässt sich nicht als Begründung für "pythonisch" oder nicht heranziehen, da es im Kern eine C++-API ist.

Ach? Na, mich würde ja mal interessieren, wie du eine solche API dann "pythonisch" entwirfst ... abgesehen davon, dass man Indirektionen überhaupt nicht vermeiden kann, wenn mehrere gleichnamige Attribute auf verschiedenen Ebenen existieren. Und ob es jetzt pythonisch ist, alle Bestandteile einer API wie der QTextDocument-API in eine einzige Klasse zu packen, sei mal dahingestellt ...
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Mittwoch 2. Juli 2008, 13:14

Hi Lunar!

Ich kann Blackjacks Argumente gut nachvollziehen, vor allem das Problem der nachträglichen Änderungen und ja, ich fragte nach pythonisch.
Wenn man gut gepflegte Klassen- und Beziehungsdiagramme hat, dann kann man ja vielleicht noch nachvollziehen, welche Instanzen (evtl. aus anderen Modulen) auf ein Attribut zugreifen und evtl. anzupassen sind. Wenn man aber nur Code hat, ist man schon fast verloren.

lunar hat geschrieben:
BlackJack hat geschrieben:@lunar: Die Qt4-API lässt sich nicht als Begründung für "pythonisch" oder nicht heranziehen, da es im Kern eine C++-API ist.

Ach? Na, mich würde ja mal interessieren, wie du eine solche API dann "pythonisch" entwirfst ...

Ich gehe davon aus, dass Blackjack zwischen API unterscheidet, die zum einen mit der C++-API "verwandt" sind und jenen, die von Grund auf nur für ein Python-Modul konzipiert sind und die pythonischen Grundsätze beim Design respektieren.

lunar hat geschrieben: abgesehen davon, dass man Indirektionen überhaupt nicht vermeiden kann, wenn mehrere gleichnamige Attribute auf verschiedenen Ebenen existieren.

Da bin ich anderer Meinung. Denn C wird ja direkt nur von B referenziert und hat erstmal nichts mit A zutun. Wenn A also ein Attribut von C haben möchte, dann kann B durch seine Implementierung bestimmen, ob es das preisgeben möchte.
also:

Code: Alles auswählen

fleet.get_ship_name(ship_id)
fleet.get_crewmember_name(ship_id, crewmember_id)

Ob das schön ist, sei dahingestellt, aber es ist möglich.
Problem ist nur (wenn man die Zahl der erreichbaren Attribute nicht stark einschränkt), dass diese Methoden kaskadierend implementiert werden müssen, was zu rapide wachenden Klassen führt.

Gruß,
der Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...
Benutzeravatar
Michael Schneider
User
Beiträge: 567
Registriert: Samstag 8. April 2006, 12:31
Wohnort: Bremen
Kontaktdaten:

Beitragvon Michael Schneider » Mittwoch 2. Juli 2008, 13:30

Nachtrag:

Bei Verwendung von Methoden der Instanz A um auf Attribute von B und C zuzugreifen, kann man eine weitere große Stärke von Python hervorragend ausspielen: die Verwendung von mapping/filtering und diversen iterierbaren Objekten (für die Argumente).

Michel
Diese Nachricht zersört sich in 5 Sekunden selbst ...

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder