Anzeigemethode im angezeigten Objekt ablegen?

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.
Antworten
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hallo,

ich hab' da mal wieder so ein Konstrukt im Kopf, von dem ich mir nicht sicher bin, ob es 'sauber' ist... :wink:

Ich habe ein Objekt, das mehrmals am Bildschirm angezeigt werden soll. Wird das Objekt geändert, sollen natürlich auch die verschiedenen Anzeigen darauf reagieren. Um das zu erreichen, würde ich die Methode zum Anzeigen des Objektes einfach im selbigen hinterlegen. So eine view-Liste könnte beispielsweise folgende Elemente enthalten:

Code: Alles auswählen

[(view1.show, (0, 0)), (view2.show, (5, 2))]
Sobald das Objekt sich ändert, übergibt es sich selbst und die hinterlegten Koordinaten an die jeweilige view-Methode. Im gezeigten Beispiel würde das Objekt also in view1 auf 0, 0 und in view2 auf 5, 2 angezeigt werden.

Was mich ein wenig daran stört ist, dass hier eine Anzeige-Methode in einem Datenobjekt hinterlegt wird.
Sind solche Vermischungen von Anzeige und Daten nicht eher unsauber?
Können dabei Probleme entstehen, von denen ich noch nichts weiß?
Oder ist das ok und kann so gemacht werden?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mkiever
User
Beiträge: 19
Registriert: Mittwoch 4. März 2009, 18:06
Wohnort: Braunschweig

Hallo,

da sollte man mal das Observer Pattern nachlesen.
Wo die Verknüpfung gemacht wird, ist aber umstritten
(von einem View oder von einem Controller aus ist üblich).
Ich selbst baue solche Observer Verknüpfungen von außen,
einem übergeordneten Applikationsobjekt, auf.
Also in etwa:

Code: Alles auswählen

   app._data.addObserver(app._view1)
   app._data.addObserver(app._view2)
wobei _data das Datenobjekt und _view1/2 die Anzeigen sind
und dem Applikationsobjekt app gehören und auch dort erzeugt werden.

Ich hoffe das hilft weiter,
Matthias Kievernagel
BlackJack

@mutetella: Was in Ordnung ist, wäre ein Rückruf bei dem sich irgendwer registrieren kann, wenn das Datenobjekt irgendwem eine Änderung mitteilen will. Wie man an "irgendwer" und "irgendwem" vielleicht erkennen kann, ist das unabhängig von GUI. Natürlich kann sich da *auch* GUI-Code registrieren. Solange die Implementierung des Daten-/Logikobjekts kein Wissen darüber braucht, um wen es sich bei dem zurück gerufenen handelt, ist das sauber.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

BlackJack hat geschrieben:Solange die Implementierung des Daten-/Logikobjekts kein Wissen darüber braucht, um wen es sich bei dem zurück gerufenen handelt, ist das sauber.
Ich verstehe nicht so recht, wie Du das meinst. Wenn das Datenobjekt eine Methode zum Aufnehmen eines Rückrufes besitzt, hat das ja erstmal sowieso nichts mit einem potentiell zurückgerufenen Objekt zu tun. Anderst gefragt: Wie sähe eine Implementierung aus, die Wissen über das zurückgerufene Objekt braucht?

Ich habe mir jetzt auch bereits ein paar Beispielmodelle angeschaut. Allerdings handelt es sich bei den zurückzurufenden Objekten in diesen Beispielen immer um Objekte wie Buttons, TextCtrl's etc., die Informationen ihrer Bildschirmposition in sich speichern.
Wenn ich aber meine Daten in einer Konsole ausgebe, habe ich solche weiterverwendbaren Objekte ja erstmal nicht.
Ist es in einem solchen Fall in Ordnung, wenn ich Parameter wie z. B. Koordinaten, die genau genommen nicht zum Datenobjekt gehören, zusammen mit der zurückzurufenden Methode ablege. Eben so, wie in meinem kleinen Beispiel?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Naja wenn das Datenobjekt zum Beispiel wüsste das es sich um GUI-Objekte handelt und am ende darauf auf noch eine `update()`-Methode aufrufen würde um die Anzeige zu aktualisieren, dann wäre das nicht sauber.

Das mit den Argumenten ist auch in Ordnung denn Dein Datenobjekt weiss ja nicht was die bedeuten.

Man kann das alles übrigens ein wenig abstrahieren durch ein `Event`-Objekt, wo man Rückruffunktionen registrieren kann. Ungetestet:

Code: Alles auswählen

class Event(object):
    def __init__(self):
        self.callbacks = set()
    
    def __call__(self, *args, **kwargs):
        for callback in self.callbacks:
            callback(*args, **kwargs)
    
    def add(self, listener):
        self.callbacks.add(listener)
    
    def remove(self, listener):
        self.callbacks.remove(listener)
Das kann man wiederverwenden. Und bei APIs die nur eine einfache Rückruffunktion erwarten, kann man es verwenden um mehrere Funktionen daran zu binden. Dann muss man es allerdings ausserhalb der API verwalten. Man kann die Dinger sogar schachteln, also ein `Event`-Exemplar einem anderen als "listener" hinzufügen.

Argumente wie Deine Koordinaten müsste man hier zum Beispiel mit `functools.partial()` integrieren: ``data.changed_event.add(partial(view2.show, 5, 2))``.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack: Vielleicht eine dumme, für mein Verständnis aber wichtige Frage: Wie wird denn ein solches Event ausgelöst? Ich kenne das von wxPython, wo ich mittels 'Bind()' ein event an ein bestimmtes widget binden kann.
Hier würde ich folgendermaßen vorgehen:

1. Jedesmal, wenn mein data-Objekt angezeigt wird, registriert sich die view, so oder so ähnlich wie in Deinem Beispiel: 'data.changed_event.add(partial(view2.show, 5, 2))'
2. Wenn das data-Objekt geändert werden soll, wird die entsprechende view zur Änderung aufgerufen.
3. Diese view kann ja entweder abgebrochen oder mit einem 'OK' beendet werden. Wird über 'OK' beendet, wird 'data.changed_event()' aufgerufen.

Ist das so korrekt?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Das musst Du "auslösen" in dem Du es aufrufst -- siehe die `__call__()`-Methode. Wenn Dein Datenobjekt die Views informieren will, bzw. allgemeiner eben alle die eine Funktion registriert haben, dann ruft es ``self.changed_event()`` auf. Das hat nichts mit wxWidgets-Events zu tun.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack: Schon klar, dass das nichts mit wx zu tun hat. Ich habe das nur ins Spiel gebracht, weil ich dort eben ein 'data.changed_event()' an den OK-Button binden würde, mit dem ich eine Änderung der Daten bestätige.
Wäre folgende Vorgehensweise in Bezug auf die Trennung von Daten und Anzeige korrekt?

Code: Alles auswählen

class View(object):
    ...
    def change_data(self, data):
        ....
        ok = window.getkey()
        if ok in 'jJ':
            data.changed_event()
Mich hat das 'self' in Deiner letzten Antwort verwirrt.

Sorry für meine Korinthenkackerei, aber diese kleinen Details sind für mich immer sehr wichtig...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Events werden eigentlich von den Objekten "ausgelöst" auf denen sie definiert sind. Darum auch das `self`. "Listener" oder "observer" registrieren sich ja auf dem Objekt um von dem Objekt informiert zu werden, wenn sich etwas Interessantes getan hat. Wenn Du auf eine "Ok"-Schaltfläche in der GUI reagieren willst, dann ist das ja eine reine GUI Geschichte, die hat mit den Daten erst einmal nichts zu tun.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack: Ok... vielen herzlichen Dank! Das war mir mal wieder eine große Hilfe!

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@BlackJack:
Ok, eine Frage habe ich noch: Weshalb benutzt Du in Deinem Event()-Beispiel statt einer Liste ein set?
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Warum nicht!? :-) Ist halt die Frage was für Eigenschaften man haben möchte. Also zum Beispiel Reihenfolge der Aufrufe und was passieren soll wenn man die gleiche Rückruffunktion mehrfach registriert und wieder entfernt.
Antworten