Ist das eine sinnvolle Programmstruktur?

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' mir jetzt mal erste Überlegungen über eine mögliche Struktur meines Kalenderprogramms gemacht. Die Darstellung hänge ich hier mal mit an.

http://www.denkdran.org/img/denkdran.jpg

Wie ich mir das vorstelle:

- Die Funktion 'main()' erstellt eine Instanz von MainFrame() und übergibt config-Daten an diese.
- Dort wird eine Instanz von View() erstellt, die wiederum eine Instanz von ViewConstructor() erstellt.
- Innerhalb der View()-Instanz liegen dann diverse Item()-Instanzen, in denen sich ItemConstructor()-Instanzen befinden.

Alle Informationen zum Darstellen der view und der darin enthaltenen items werden von den Constructor-Instanzen erstellt und zur Verfügung gestellt.

Soll ich auf diese Weise weiterdenken oder bin ich auf dem Holzweg?

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

@mutetella: Ich verstehe die Constructor-Exemplare nicht!? Und ansonsten ist das so superallgemein gehalten, dass es wahrscheinlich genau das richtige, aber vielleicht auch nicht so günstig sein kann.

Ich persönlich modelliere ja lieber immer die Logik zuerst und setze dann die GUI dort drauf. Natürlich muss man sich über die GUI als Schnittstelle zur Logik auch Anfangs schon Gedanken machen, aber eher unter dem Gesichtspunkt welche Funktionalität die Logik später erfüllen muss, weniger wie die GUI konkret implementiert wird.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Als erstes verwirfst du dein Diagramm mal komplett ;-) Dann entwirfst du ein neues, welches ohne GUI oder Konsoleneingaben auskommt und befragst Google mal nach MVC. Bei deinem jetzigen Vorhaben vermischt du nämlich ganz ordentlich die Daten, die Logik und die GUI.

Sebastian
Das Leben ist wie ein Tennisball.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

BlackJack hat geschrieben:Ich verstehe die Constructor-Exemplare nicht!?
Die sollen dafür zuständig sein, die view mit allen nötigen Parametern (Koordinaten, Texte) zu versorgen.
BlackJack hat geschrieben:Ich persönlich modelliere ja lieber immer die Logik zuerst und setze dann die GUI dort drauf.
EyDu hat geschrieben:Dann entwirfst du ein neues, welches ohne GUI oder Konsoleneingaben auskommt...
Ich denke, Ihr habt Recht. Klingt sehr nachvollziehbar und fühlt sich auch gut an...
EyDu hat geschrieben:...und befragst Google mal nach MVC.
Ich wollte einfach mal wieder mit meinem Halbwissen loslegen...

Nichstdestotrotz werde ich mit meinem kommenden Exemplar bei Euch anklopfen... :-)

Gruß
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:

Model und View strikt zu trennen fällt mir wirklich schwer, puh... zu sehr hänge ich gedanklich noch daran, wie das Programm einmal ablaufen könnte. Und dabei dominiert die View noch stark in meinem Kopf... :-)

Während ich mich mit der Materie beschäftige, sind folgende Fragen schon mal aufgetaucht:

- Berechnung der Koordinaten einzelner View-Objekte.
Das Model-Objekt kann ja, ohne ein View-Objekt zu fragen, schon mal überhaupt nicht wissen, wie groß das Frame ist, auf dem dann alles stattfindet. Also kann dieser Bereich nicht gänzlich im Model-Objekt durchgeführt werden. Oder wie, oder was?
- Ein Mausklick findet statt.
Das Abfragen der Koordinaten beim Event-Objekt ist Sache des Controllers. Und das Ermitteln des Objektes, das sich hinter den Korrdinaten befindet? Controller oder Model?
- Ein Login.
Der Nutzername samt Passwort landet aus einem View-Objekt im Controller. Wo findet die Überprüfung auf Übereinstimmung statt?

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

@mutetella: Das Model gehört zu der Problemdomäne, also hier "Kalender". Da denkt man in so Begriffen wie "Termin"/"Eintrag" und "Kalender" als Container für Termine/Einträge. Das ist unabhängig von GUI und dementsprechend sind irgendwelche Frame-Grössen auch völlig egal an der Stelle.

Mausklicks und Koordinaten sind dem Model auch fremd. Das ist alles GUI. Dass es zum Beispiel nicht ein Widget pro Tag ist, sondern Du eine Monatsansicht auf einem Widget selber zeichnest, hat ja auch überhaupt nichts mit der Programmlogik zu tun, sondern ist eine reines Implementierungsdetail in der GUI.

Bei der letzten Frage muss man wissen wie das mit dem Login geregelt ist. Das ist IMHO von der Kalender-Logik erst einmal unabhängig, es sei denn Du hast dort auch schon Benutzer und Zugriffsrechte modelliert.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

BlackJack hat geschrieben:Da denkt man in so Begriffen wie "Termin"/"Eintrag" und "Kalender" als Container ... und dementsprechend sind irgendwelche Frame-Grössen auch völlig egal an der Stelle.
Ich bin da immer noch viel zu sehr auf Abläufe (Programmstart - Fenster erzeugen - KlickiKlicki - Programmende) fixiert.
BlackJack hat geschrieben:... Koordinaten sind dem Model auch fremd. ...
Also "dürfen" innerhalb des View-Bereichs auch Berechnungen (sofern sie die View betreffen) stattfinden? Ich dachte, die View ist ausschließlich zur Erzeugung einer Anzeige zuständig. Die dafür nötigen Daten (und die Erzeugung eben dieser) kommen von außerhalb.
BlackJack hat geschrieben:... es sei denn Du hast dort auch schon Benutzer und Zugriffsrechte modelliert.
Nein, habe ich nicht. Die Frage war eher theoretischer Natur.
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: Vielleicht hilft es ja, wenn Du parallel zur GUI auch eine einfache kommandozeileorientierte Schnittstelle zu Deiner Programmlogik implemnetierst und/oder Unit-Tests für die Logik schreibst.

Berechnungen die mit der Anzeige zu tun haben, dürfen selbstverständlich im View durchgeführt werden.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Mach' mir gerade Gedanken über eine Klasse Diary(), die erstmal "nur" dafür zuständig sein soll, Termine aufzunehmen und über diese dann abgefragt werden kann.

Ein Termin wiederum ist eine Instanz der Klasse Entry().

Nachdem Diary() derzeit lediglich eine Liste mit Entry()-Instanzen enthält taucht für mich die Frage auf, ob es eventuell Sinn macht, Diary() zu einer subclass von list zu machen. Hätte das Vorteile?

Code: Alles auswählen

class Diary(object):
    def __init__(self):
        self.entries = []

    def add(self, entry):
        self.entries.append(entry)

    def get_entries(self, day):
        entries = []
        append = entries.append
        for entry in self.entries:
            if entry.date == day:
                append(entry)
        return entries

class Entry(object):
    def __init__(self, date, title, begin, end, categorie, longtext):
        self.date = date
        self.title = title
        self.begin = begin
        self.end = end
        self.categorie = categorie
        self.longtext = longtext
Meine erste Überlegung, von der das obige übrigblieb, war:
Ein Diary() enthält Day()'s, die wiederum Entry()'s enthalten. Dadurch wäre eine Abfrage nach allen Objekten zu Tag xx einfacher. Zudem hat das Datum von meinem Gefühl her nichts in Entry() verloren, da ein Entry ja nicht immer ein tagesgebundenes Objekt sein muss. Andererseits ist aber die Gefahr einer unnötigen Verschachtelung gegeben. Deswegen habe ich dann Day() wieder verworfen. Bin mir aber einfach nicht sicher, wie denn so ein Container am besten aufgebaut werden könnte.

Na ja, ich bräuchte eine Wegweisung... :-)

Gruß
mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Also im Moment würde ich auf die Diary-Klasse komplett verzichten und einfach eine Liste nutzen ;-)

Du solltest Dir mehr Gedanken darüber machen, worin sich die Entries unterscheiden könnten. Neben einfachen Dingen wie "Tagging" hast Du ja schon Dinge wie "Dauer", "Wiederholung" usw. angesprochen. Evtl. lohnt es sich dafür eigene Typen zu entwickeln, die ein Entry-Objekt in einem Attribut speichert?

Mir fielen da spontan Dinge ein:
- Termin wiederholt sich alle x Tage, insgesamt y Mal
- Termin findet an Sequenz von x Tagen statt.
- Termin dauert x Tage am Stück
- evtl. Kombination obiger typen möglich?

Nun stellt sich die Frage, wie man so etwas dann im Kalender speichert. Man könnte z.B. in dem Kalender nur Verweise auf ein Entry-Objekt speichern. Somit könnte beim Neu-erstellen eines Termins mit Sequenz einfach die Sequenz generiert werden, jedoch eben nicht aus Entry-Objekten, sonder aus Objekten mit Verweis auf dasselbe Entry-Objekt. Allerdings bräuchtest Du dann eine "update"-Funktionalität für den Kalender, falls sich etwas am Entry-Objekt ändert.

Bei der Überlegung würde ich an der Namensgebung feilen und "Entry" in "Event" umwandeln. Das Kalender-Objekt enthielte dann für jeden Tag eine Liste mit Objekten vom Typ Event. Diese hätten intern ein oder ggf. mehrere Typen, die die Zeitrestriktionen charakterisieren. Bei Änderungen an diesen (z.B. 4 statt 6 Wiederholungen) müßte man eine Art upate-Funktionalität implementieren, die die nun ungültigen Verweise aus dem Kalender-Objekt entfernt.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Hyperion hat geschrieben:Evtl. lohnt es sich dafür eigene Typen zu entwickeln, die ein Entry-Objekt in einem Attribut speichert?
Meinst Du damit sowas?

Code: Alles auswählen

class Entry(object):
    def __init__(self, date, title, begin, end, repeat, 
        categorie, longtext):
        self.date = date
        self.title = title
        self._repeat = repeat
        self.begin = begin
        self.end = end
        self.categorie = categorie
        self.longtext = longtext

    def set_repeat(self, repeat):
        self._repeat = repeat

    def get_repeat(self):
        return [self.date + Timedelta(i) 
            for i in xrange(1, self._repeat + 1)]

    repeat = property(fget=get_repeat, fset=set_repeat)
Hyperion hat geschrieben:Allerdings bräuchtest Du dann eine "update"-Funktionalität für den Kalender, falls sich etwas am Entry-Objekt ändert.
Ist nicht spätestens dann eine Kalender-Klasse wieder sinnvoll?
Hyperion hat geschrieben:Bei der Überlegung würde ich an der Namensgebung feilen und "Entry" in "Event" umwandeln.
Mir war/wäre eine Event() immer schon sympathischer als eine Entry(). Allerdings habe ich bedenken, ob ich da später nicht ein wenig Verwirrnis schaffe, spätestens dann, wenn events aus der GUI verarbeitet werden. Bin noch am darüber nachdenken...
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
BlackJack

@mutetella: `Diary` als Unterklasse von `list` hätte IMHO keine Vorteile -- im Gegenteil. So eine Vererbung ist ja eine "ist ein(e)"-Beziehung, also ist die Frage ob ein Kalender eine Liste ist. Und da würde ich klar Nein sagen, denn die Operationen die man auf einem Kalender haben möchte, decken sich nicht mit denen einer Liste, und die Liste hat Funktionen, die bei einem Kalender nicht viel Sinn machen.

Man kann die Termine intern in einer Liste halten, man könnte einen Kalender aber auch als Abbildung von Datum auf Termine ansehen. Wenn der Kalender jetzt von `list` erbt, wird die Verwaltung in einem Dictionary ziemlich schräg, den Weg würde man sich damit also verbauen. Mit wiederkehrenden Terminen könnte es sogar Sinn machen intern verschiedene Arten von Terminen in verschiedenen Datenstrukturen zu halten.

`Entry` oder `Event` hat ein `date`, und `begin` und `end` -- was sind denn das für Typen? Kann das so wie's da steht `Event`\s abbilden die um 10 vor Mitternacht anfangen und am nächsten Tag um 2 Uhr nachts enden? Und gibt `Diary.get_entries()` das `Event` auch für beide Tage zurück? Sieht im Moment nicht so aus. Da würde sich für die Zeit(spanne) wahrscheinlich sogar schon ein eigenes Objekt `Timespan` oder so lohnen. Mit Operationen zum Vergleichen, Testen ob ein bestimmter Tag enthalten ist, ob sich zwei Zeiträume überlappen, usw.

Das mit `append` würde ich übrigens als unnötige Mikrooptimierung ansehen die in diesem Stadium noch nichts im Quelltext zu suchen hat. Ich würde das auch mindestens als "list comprehension" ausdrücken, noch lieber aber als Generatorausdruck. Also mit dem zuvor gesagten so was in dieser Richtung (ungetestet):

Code: Alles auswählen

    def get_entries(self, day):
        return (e for e in self.events if day in e.timespan)
Bei `get/set_repeat()` brichst Du mit üblichen Erwartungen. Wenn ich ``a.x = 42`` schreibe, dann bin ich sehr überrascht, wenn ``print a.x`` eine Liste ausgibt. Attribute sollten "symmetrisch" sein und beim setzen und abfragen zumindest den gleichen "duck type" verwenden.

Wie Du mit den Wiederholungen umgehst ist letztendlich eine Implementierungsentscheidung. Du kannst für x Wiederholungen tatsächlich x Events erzeugen und speichern, oder sich wiederholende Termine extra behandeln auf Anfrage aus *einer* Beschreibung berechnen ob der Termin auf einen gegebenen Tag zutrifft.

Interesannte Fragen in dem Zusammenhang:

1. Wie geht man mit unendlich vielen Terminen um? Sagen wir ich schaue Sonntags grundsätzlich Tatort und gebe nächsten Sonntag als Starttermin, wöchentliche Wiederholung, und kein Enddatum an, weil ich ja nicht weiss welcher der vielen Weltuntergangskalender von antiken Kulturen der Tatort-Reihe ein Ende setzen wird.

2. Wie geht man mit Ausnahmen bei sich wiederholenden Terminen um? Sagen wir der Tatort fällt wegen Fussball aus oder wird verschoben, dann möchte der Benutzer ja gezielt nur den einen Termin löschen oder verschieben.

Um die `Event`\s nicht zu verwechseln gibt's ja Namensräume.
Antworten