Analyse und Aufbau einer App in OOP

Eigentlich ist mir die Frage peinlich, aber es kann sonst niemand helfen ... :)

Moderator: Moderatoren

Antworten
Daniel

Analyse und Aufbau einer App in OOP

Beitrag von Daniel »

Wie man eine Applikation früher aufgebaut hat, weiss ja wohl jeder hier.
Die Frage wäre nun, wie gliedert man eine Fragestellung für die OOP auf?

Nehmen wir das altbekannte Bsp einer Adressverwaltung. Alt:
- PROC Main: Menu: - erfassen, - mutieren, löschen, - auflisten, - drucken, - Ende.
(vielleicht noch Filter +Scope)
- PROC Erfassen, FUNC Maske,
- PROC Mutieren, FUNC Suchen, (FUNC Maske)
- PROC Löschen, (FUNC Suchen), (FUNC Maske)
- PROC Selektion, Proc Auflisten, Proc Drucken
Oder es kann ein schöneres Bsp sein ...

Nun, wie würde man das für OOP aufgliedern/ zerlegen?
Welche Klassen, Methoden?
Welche Objekte?

Ev. ein paar nachvollziehbare Beispiele?
Muss ja nicht gleich eine funktionsfähige, vollständige App sein, kann auch nach +nach entstehen?
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21164
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Hi Daniel,

dann mache ich mal den Anfang.

Zuerst würde ich mir eine Klasse für die Datenbanken bauen.

Dazu Methoden

1) DB öffnen / Index öffnen (automatisch prüfen, ob alles vorhanden und auf dem aktuellen Stand ist)
2) Aus Tabelle in Membervar schreiben
3) aus Membervar wieder zurück in DB mit entsprechenden Möglichkeiten einen Satz anzuhängen, oder aber den bestehenden zu überschreiben (Sperren und Freigeben nicht vergessen)

Var, die für alle DB gelten werden (z.B. ID)
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21164
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

2.

Dann würde ich mir Klassen für jede DB anlegen, in der die spezifischen Daten wie Felder, Indexschlüssel, Dateiname, Verzeichnisname usw stehen.

Hier würde ich auch die entsprechenden Masken und Abfragefelder einbringen.

PS:leider kann man hier im Forum nicht "unbegrenzt" große Beispiele einstellen, weil sonst in dem Thread nix mehr läuft.
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Daniel

Beitrag von Daniel »

Manfred (und Alle)

wenn ich dich richtig verstanden habe, kämen die Masken (Dialoge) bei dir in die DB-Klasse.

Ein anderer Ansatz, den ich kennen gelernt habe, würde jetzt vom Alaska-Formdesigner (FD) ausgehen.
Hier wäre jeder Dialog ein PRG mit einer Dialogklasse.

Erstelle ich den Dialog "AdresseNeu" mit seinen STATIC- (Text) und SLE-Feldern mit dem FD und speichere ihn als "AdresseNeu.XFF", dann kann ich ja auch den CLASS-Code generieren als "AdresseNeu.PRG".
Dieser enthält dann bereits ein Gerüst zur Implementierung, bestehend aus

Code: Alles auswählen

CLASS AdresseNeu FROM _AdresseNeu
     EXPORTED:
       METHOD init
       METHOD create
ENDCLASS

- Weiter das Gerüst der METHODS

- Dann die PROCEDURE Main mit dem Aufruf:

AdresseNeu():New():Create()

gefolgt vom Eventloop
Ihr kennt das wahrscheinlich. Einen Nutzen sollte das ja haben.

1. Wie beurteilt ihr das, wenn man diesen Raster verwendet und eine Klasse für jeden Dialog macht?
2. Kann man die Dialoge dann überhaupt ableiten? Oder kopiert man einfach die Funktionalität, die sich gleich oder ähnlich bleibt.
3. Setzt man dann auch eine DB-Klasse ein, oder eine FUNCTION wie Net_Use() innerhalb der METHOD create?

Viele Fragen! Ich hoffe, nicht zu viele.
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21164
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Hi Daniel,

so habe ich es jetzt gemacht.

Das was der Formdesigner macht, habe ich noch nicht wirklich durchblickt. Aber ich glaube damit stehe ich hier auch nicht ganz alleine..
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Benutzeravatar
Martin Altmann
Foren-Administrator
Foren-Administrator
Beiträge: 16501
Registriert: Fr, 23. Sep 2005 4:58
Wohnort: Berlin
Hat sich bedankt: 111 Mal
Danksagung erhalten: 48 Mal
Kontaktdaten:

Beitrag von Martin Altmann »

Hallo Manfred,
nun - im Prinzip erleichtert der Form-Designer den Einstieg in die OOP.
Er erzeugt zwei .prg-Dateien - eine mit dem Namen, den Du eingibst und eine mit einem dem Namen vorangestelltem _.
Im Prinzip ist die Datei mit dem _ die Superklasse und die ohne _ die Subklasse (also von der mit _ abgelitten).
Die Datei mit dem _ enthält im Prinzip nur das Layout - welche Xbase++-Parts liegen wo und heißen wie. Diese Datei wird immer wieder überschrieben, wenn im Form-Designer an der entsprechenden .XFF-Datei Änderungen vorgenommen werden.
Die Datei ohne dem _ enthält die von Dir nachträglich hinzuzufügenden Erweiterungen - also z.B. valid-Bedingungen oder die Methoden, die beim Anklicken eines Knopfes ausgeführt werden sollen ...

Viele Grüße,
Martin
:grommit:
Webseite mit XB2.NET und ausschließlich statischem Content in Form von HTML-Dateien: https://www.altem.de/
Webseite mit XB2.NET und ausschließlich dynamischem Content in Form von in-memory-HTML: https://meldungen.altem.de/

Mitglied der XUG Osnabrück
Vorsitzender des Deutschsprachige Xbase-Entwickler e. V.
Daniel

Beitrag von Daniel »

Hallo Martin

dann kann man also davon ausgehen, dass sich mit diesem Prinzip arbeiten lässt.
Bei grösseren Masken ist der FD nämlich schon praktisch, denn da kann man neue Felder einfügen und allenfalls alte noch etwas schieben, ohne das ganze Layout x-mal umzuschreiben (zumal man mit diesen neuen Pixelkoordinaten mit dem seltsamen Bezugssystem ja nicht mehr richtig arbeiten kann).

Und wie du schreibst, bleibt das Layout in der _Datei.prg erhalten, während man seine Erweiterungen in die Datei.prg schreibt. Beides ist also automatisch getrennt (und heisst doch gleich).

Manfred,

Ich bin auch interessiert, wie du das machst, denn natürlich führen viele Wege nach Rom, und es geht hier ja auch drum, Möglichkeiten, Vor- und Nachteile gegen einander abzuwägen.

Hat wer noch weitere Vorschläge?
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Hi,

ich habe mir vor Jahren das alte MDI Beipspiel genommen und nach meinen Wünschen angepaßt. Dieses enthält den About Bildschirm, das normale Menüsystem (ohne spezielle Aufrufe für meine neuen Fenster) und einige Grundfunktionen etc.

Dieses kopiere ich jeweils für jedes neue Projekt in einen neuen Ordner, wobei intern eine CMD Datei existiert, welche die allgemeinen Dateien (werden zentral gepflegt) durch copy auf den neuesten Stand bringen wenn ich was geändert habe.

Neue Fenster erstelle ich dann mit dem Formdesigner (oder kopiere sie aus einem anderen Projekt wenn es einigermaßen passt) und erstelle CLASS-Code. Diesen kopiere ich in das neue Programmverzeichnis.

Dieser ist zwar ganz nett, aber er hat mehrere Nachteile, die wohl zu 100% auf den Einschränkungen des XppFD beruhen:

Einschränkungen im CLASS-CODE
  • - Kein REDO möglich, daher immer Gefahr von Falscheingaben (verschieben, Größenordnungen etc.)
    - Letztes bearbeitetes Controll erhält zuerst Fokus (zumindest in der 1.82)
    - Selektion verschiedener Controlls ist nicht zuverlässig, wobei immer die Gefahr der Verschiebung bzw. Größenänderung droht - und es gibt kein REDO ...
    - Ich bin nicht sonderlich im positionieren mit der Maus (ok die Ausrichtungsbuttons können helfen ...), aber ich sehe jeden Pixel Unterschied in der Anwendung ;-)
    - Späteres Verschieben ist nicht ganz so einfach wie es sein sollte.
    - Meine Umgebungsklasse hat Erweiterungen, die die neuen Fenster brauchen.
Daher tausche ich die Erbinformationen XbpDiolog gegen meine eigene Klasse aus und tausche die absoluten Pixelangaben durch Verweise auf lokale Variablen, die die aktuelle Zeilenposition kennen und danach angepasst werden.

Hierbei habe ich 3 Möglichkeiten, die alle Nachteile haben:

1. in der INIT-Methode, da weiß ich aber noch nicht wie groß das Fenster wirklich sein wird (ich kann also nicht von der aktuellen Größe berechnen)
2. in der CREATE-Methode, aber dann steht in der INIT alles fix und hier wird wieder geändert. Außerdem entscheidet die create Reihenfolge über die TAB-Reihenfolge, diese muß aber nicht mit der Bildschirmanordnung übereinstimmen ...
3. in der resize methode SETPOS und SETSIZE (geht mittlerweile auch in einem Befehl) nach Neuberechnung setzen, das hat auch den Vorteil, dass ein Ändern der Fenstergröße behandelt wird.

Ich denke mittlerweile, dass die 3. Methode die beste ist, mache es in meinen Programmen aber je nach Entstehungszeit.

Ein Beispiel:

in einer CH Datei werden die Standardmaße definiert:

#define nSLEHoehe 24 (die Werte jetzt frei aus dem Kopf)
#define nSLEVorschub 30
#define nSLEBreite 100

in der Classe steht alter Code:

oSLE := XbpSle1():new(...., 7, 560, ....
oSLE := XbpSle2():new(...., 6, 430, ....
oSLE := XbpSle3():new(...., 8, 300, ....

(nur so als Beispiel, die x - Werte sollten eigentlich gleich sein, aber die Ausrichtung wurde vergessen ;) )

nun erstelle ich daraus eine Formel:

nPosX := 7
nPosY := 100
oSLE := XbpSle1():new(...., nPosX, nPosY, ....
nPosY -= nSLEVorschub
oSLE := XbpSle2():new(...., nPosX, nPosY, ....
nPosY -= nSLEVorschub
oSLE := XbpSle3():new(...., nPosX, nPosY, ....
oSLE := XbpSle4():new(...., nPosX+200, nPosY, .... // gleiche Zeile
nPosY -= nSLEVorschub

natürlich alles in LOCAL Variabeln !

Wenn ich nun eine neue Zeile dazwischen einfügen will mache ich es einfach:

nPosX := 7
nPosY := 100
oSLE := XbpSle1():new(...., nPosX, nPosY, ....
nPosY -= nSLEVorschub
oSLE := XbpSle2():new(...., nPosX, nPosY, ....
nPosY -= nSLEVorschub
oSLE := XbpSle2NEU():new(...., nPosX, nPosY, ....
nPosY -= nSLEVorschub

oSLE := XbpSle3():new(...., nPosX, nPosY, ....
oSLE := XbpSle4():new(...., nPosX+200, nPosY, .... // gleiche Zeile
nPosY -= nSLEVorschub

durch die Variable Positionierung kann ich den Rest ganz leicht Verschieben, auch ein Abspeichern und Laden aus INI-Dateien wäre möglich, aber das mache ich nicht.

Der einzige Nachteil ist, dass man so nie wieder mit dem XppFD an diese Datei gehen darf und man sich alles im Kopf vorstellen muß wenn man daran arbeitet.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Ich hoffe ja, dass VX passable Grundgerüste mitliefert und werde dann mal mein eigenes überdenken. Ein deutlich besserer Painter wäre nicht schlecht.
Gruß
Hubert
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Analyse und Aufbau einer App in OOP

Beitrag von brandelh »

Daniel hat geschrieben:Wie man eine Applikation früher aufgebaut hat, weiss ja wohl jeder hier. Die Frage wäre nun, wie gliedert man eine Fragestellung für die OOP auf?
Manche machen aus der MAIN Procedure eine Klasse, die alle nötigen Infos enthält, ich habe schon zentral benötigte Infos in einer eigenen APPSYS AppClass abgelegt (code habe ich jetzt nicht hier), aber das kann man auch in Funktionen legen (diese merken sich dann die Infos in einer STATIC Variablen). Ich meine, alles neue ansehen und das verwenden, was einem nützt.

Wenn man an verschiedenen Stellen nur lesend auf zentrale Werte zugreifen muss (Rechnername, Angemeldeter Benutzer etc.) kann man meiner Meinung nach ruhig auch publics nehmen. Der Aufruf einer Funktion (und sei es nur um das Programmobject zu erhalten) dauert auch Zeit.

Hauptprogramm ist wie gehabt mit Eventloop und startet das Hauptfenster.
Menüsystem ist natürlich neu und in dem Hauptfenster.

JEDES Fenster wird in OOP CLASS Code generiert und nötigenfalls abgeändert. Alles was dieses Fenster können muss kommt in Methoden. Sollten 2 Fenster sehr ähnlich sein, könnte man über Ableiten nachdenken, aber wer sagt, dass nach dem nächsten Kundenwunsch die Ähnlichkeiten noch so groß sind ? Also nichts übertreiben.
Bei sehr vielen Fenstern könnte man über DATA-Driven Fenster nachdenke, TOM kann dazu sicher was sagen, bei mir rentiert sich das nicht.

Gedruckt wird bei mir mit Objekten meiner Druckerklasse, natürlich kann man hier auch anderes vorziehen.

Funktionen, die allgemeines tun (Mwst berechnen etc.) bleiben Funktionen. Diese werden im Classcode wie gewöhnlich aufgerufen.

WICHTIG

Es gibt Programmiersprachen (ich meine LISP war eine), bei denen ist alles ein Objekt. Es gibt Datenbanken in dem jedes Feld ein Objekt ist.
Mag sein dass das Vorteile hat, aber ich habe mich nie auf solche Diskussionen eingelassen. Was mir erkennbaren Vorteil bringt, wird in OOP geschrieben, der Rest wie ich es gerade für sinnvoller erachte.

Nur keine Glaubenskriege, es soll dem Programmierer helfen und ihn nicht knebeln. Gegensätzliches sage ich zu den Kompilerschaltern mit Warnungen etc. Hier habe ich so streng wie nur möglich eingeschaltet, da diese zwar eine gewisse Sorgfalt (LOCAL deklerationen etc.) erfordern, aber dafür die Gefahr von Tippfehlern - die einem fürchterlich Ärger bereiten können - minimieren. Somit dienen Sie dem Programmierer indem sie ihn besser führen ;)
Gruß
Hubert
Daniel

Beitrag von Daniel »

Danke, Hubert!

Da hast du uns ja einiges an Futter für die grauen Zellen geliefert.
Und dann noch in Nachtarbeit - herzlichen Dank. Du setzt dich wirklich ein für die Community.

Das alte MDI-Beispiel habe ich nicht mehr präsent, schau ich mir nochmals an.
Was den Formdesigner betrifft, so denke ich, dass man damit arbeiten, auch ändern kann, auch wenn er ein paar Mängel hat. Die fehlende UNDO / REDO Möglichkeit hast du erwähnt.
Die logische Reihenfolge der Felder aber kann man auch nachträglich noch bestimmen. Allerdings Achtung, es gibt auch hier kein Undo!

Zum Positionieren lässt sich Grid sichtbar und an Grid ausrichten einschalten. Dann eben noch die Ausrichtungs-Palette.

Worauf man noch achten muss, ist, dass man nicht in jeden Dialog einen Eventloop einbaut, sondern nur einen einzigen in der PROC Main (Main Prozedur f. Class Code lässt sich in Einstellungen auch ausschalten)

Glaubst du wirklich, dass es von Alaska mal Grundgerüste geben wird?

Nun, ich hoffe, dass sich noch der Eine oder Andere meldet mit einem Grundkonzept für die Aufgliederung des Ganzen.
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15688
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Beitrag von brandelh »

Daniel hat geschrieben:Danke, Hubert!Die logische Reihenfolge der Felder aber kann man auch nachträglich noch bestimmen.
Dass man das kann ist nicht die Frage, aber wenn ich die Reihenfolge neue festgelegt habe und danach eine Änderung an einem Control vornehme, muß ich die Reihenfolge dieses Controls erneut anpassen, zumindest war das bisher so.
Daniel hat geschrieben:Glaubst du wirklich, dass es von Alaska mal Grundgerüste geben wird?
VX muss Templates liefern, sonst wird er nicht angenommen.
JEDE andere Sprache liefert das !
Gruß
Hubert
Daniel

Gliederung: Main, Functions, Hauptfenster mit Menu, ...

Beitrag von Daniel »

Wenn wir jetzt den Aufbau der App anschauen,
dann brauchen wir mal
  • - das Main mit dem Eventloop
    - die standardmässig aufgerufenen Programme AppSys, DBESys, AppQuit
    - ev. ein Prg mit einigen Funktionen
    - das Hauptfenster mit dem Menu (Menu ev. separates Prg)
    - für jedes Fenster (Dialog) ein eigenes Prg
    - ... (was noch?)
Natürlich kann man noch weiter aufteilen, z.B.
  • - Toolbar
    - Tooltip
    - Statusbar
    ...
Manfred hat noch eine Dateiklasse vorgeschlagen?
Ist das sinnvoll, und kommt das auch in ein eigenes Prg?
Oder was sind eure Erfahrungen?
Benutzeravatar
Manfred
Foren-Administrator
Foren-Administrator
Beiträge: 21164
Registriert: Di, 29. Nov 2005 16:58
Wohnort: Kreis Wesel
Hat sich bedankt: 206 Mal
Danksagung erhalten: 67 Mal

Beitrag von Manfred »

Hi Daniel,

die Dateiklasse habe ich deswegen, weil sie vererbt und das sollte m.E. der Sinn und Zweck der Sache sein.

Du baust sie einmal und hast dann nie wieder etwas damit zu tun, solange Du nicht noch ein paar neue Ideen, bzw. Features einbauen willst.

Der Trick liegt später darin, mit wenigen Handgriffen auf bewährtes aufzusetzen und mehr oder weniger Ruck Zuck ein Programm zu bauen.
Gruß Manfred
Mitglied der XUG Osnabrück
Schatzmeister des Deutschsprachige Xbase-Entwickler e.V.
großer Fan des Xbaseentwicklerwiki https://wiki.xbaseentwickler.de/index.p ... Hauptseite
Doof kann man sein, man muß sich nur zu helfen wissen!!
Gerd König
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 193
Registriert: Fr, 09. Jun 2006 7:52
Wohnort: Nähe Sömmerda

Beitrag von Gerd König »

Hallo Daniel,

das was Manfred bezgl. einer Dateiklasse vorgeschlagen hat, haben wir in einer Basisklasse, die ich schon in einem anderen Thread erwähnt habe, realisiert.

Darin sind unter anderem alle möglichen Dateizugriffe durch einige Exported-Methoden realisiert. Diese beinhalten dabei auch bereits eine eigene Fehlerbehandlung. Da ich von allen daraus abgeleiteten Klassen Zugriff habe, kann ich diese von jeder beliebigen Stelle des Programms verwenden.

Nachfolgend schreibe ich mal einige typische Methoden auf:
- Datei öffnen (DBF, INi, TXT)
- Datei schließen
- SQL-Connect
- SQL-Disconnect
- Suche in DBF
- DBF-Datensatz lesen (über Satznr., eineindeutige Felder, eineindeutige Indexwerte
- Datensatz schreiben (wie beim Lesen)
- Datensatz anhängen
- Felder mehrerer Datensätze lesen über Index oder Feldinhalte oder Codeblock
...

Das ganze steckt in einer eigenen DLL, die dann direkt von anderen Mtarbeitern verwendet werden kann. Damit braucht sich keiner mehr mit diversen Db...-, Ordlist...- und anderen Dateifunktionen und deren Fehlerbehandlung rumzuärgern.

Viele Grüße
Gerd
Daniel

Beitrag von Daniel »

Hi Manfred

danke, das leuchtet ein.

Hi Gerd

hast du diese Basisklasse in einem eigenen PRG, das dann in eine DLL umgewandelt wird?
Oder sind in dieser DLL noch weitere Klassen, in ev. weiteren PRG's enthalten?

Mir geht es erst mal um die Strukturierung der Applikation.
Später komme gern nochmal darauf zurück, wie das in der Praxis aussehen würde, wenn ihr diese Methoden (z.B. Datei öffnen, oder suchen) in einem Programm anwendet?
Gerd König
Rekursionen-Architekt
Rekursionen-Architekt
Beiträge: 193
Registriert: Fr, 09. Jun 2006 7:52
Wohnort: Nähe Sömmerda

Beitrag von Gerd König »

Hi Daniel,

Neben der Basisklasse (ein PRG-File) habe ich an die DLL noch weiteren PRG-Code gelinkt, z.B. PRG-Files mit

- Funktionen, zur Nutzung der Methoden ohne Explizit ein Objekt zu erzeugen
- Schnittstelle zu 3d-Party-Produkten (JazzAge, See4XB)
- Schnittstellenfunktionen zu externen Systemen (SAP)

Daneben existiert noch eine eigene Pushbutton-Klasse (eine farbige Taste, die nach dem Betätigen bis zum Ende der Abarbeitung des activate-Codeblocks einen roten Rand hat, um dem User die Ausführung der Aktivität zu signalisieren.

Diese "Extras" hatte ich am Anfang in einer eigenen DLL. Da aber jede Applikation unseres PPS (fast) immer den gesamten Funktionsumfang benötigt, habe ich alles in eine DLL gepackt.

Viele Grüße
Gerd
Antworten