objektorientierte Programmierung - komponentenbasierte Programmierung

Für alles, was in den übrigen Lazarusthemen keinen Platz, aber mit Lazarus zutun hat.
PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

martin_frb hat geschrieben:
Mi 24. Feb 2021, 15:52
Erster Ansatz können Abstrakte Basis Klassen sein.
Jep. Ich zitiere mich mal selber:
PeterS hat geschrieben:
So 21. Feb 2021, 12:09
Ich habe gerade diesen Artikel gelesen .. https://de.wikipedia.org/wiki/Abstrakte_Klasse
und ehrlich gesagt habe ich mich damit nie befaßt. Wären vielleicht abstrakte Basisklassen
ein gangbarer Weg, um Lazarus- (Pascal-) TObjects voneinander zu "entkoppeln" ?
Da fehlt mir das Grundlagenwissen. :oops:
martin_frb hat geschrieben:
Mi 24. Feb 2021, 15:52
Nun kenne ich deinen speziellen Fall nicht, aber nehmen wir mal an, Audio zieht Files mit sich, weil irgendwo das Audio auf eine Liste von Files angewandt wird....
Eigentlich habe ich keinen "speziellen Fall". Ich habe erstmal nur einen Player, der aber durchaus funktioniert, mit einem geladenen File.
Sobald ich anfange, das Ding "größer" zu machen, quillt das ganze Konstrukt wie ein Hefekuchen und wird unbeherrschbar .. :roll:
Genau das, was Du da als Pseudo-Code formuliert hast, fehlt mir im Besteckkasten.
Entweder war AudioIO ein Unter-Objekt im Files-Objekt, oder umgekehrt. Anders konnte ich mir das nicht vorstellen.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von af0815 »

Wenn du über abstrakte Klassen nachdenkst, bist gleich wieder mal beim Interface. :shock:

Für mich ist - DirectShow - von MS ein Beispiel für das arbeiten auf Interfaces. Ist eine steile Lernkurve, aber zeigt ganz stark die extreme Verwendung von Interfaces. Und wie man damit abfragen kann, wer was kann.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PascalDragon »

PeterS hat geschrieben:
Mi 24. Feb 2021, 12:14
PascalDragon hat geschrieben:
Di 23. Feb 2021, 13:39
Auch bei MVC sind Interfaces im Object Pascal Sinne nicht notwendig. Ja, du brauchst eine sinnvolle Schnittstelle, aber wie genau du sie umsetzt ist nicht vorgegeben.
Das Beispiel Model GUI Mediator ist in der PDF gut erklärt,
leider aber ohne den kompletten Source-Code auf der CD/DVD
aus der damaligen TOOLBOX-Ausgabe nicht komplett nachvollziehbar.
Ich hab's gestern mal versucht, die Code-Schnipsel sinnvoll in ein Projekt zu gießen,
bin aber (noch) nicht sicher ob es tatsächlich funktioniert.
Graeme's Code ist hier verfügbar.
PeterS hat geschrieben:
Mi 24. Feb 2021, 12:14
Aber das hier gibt mir mehr zu denken:
PascalDragon hat geschrieben:
Di 23. Feb 2021, 13:39
Für die Kommunikation zwischen Threads ist in der Tat eine threadsichere Queue oder ähnliches gepaart mit Synchronisierungsmechanismen wie TEvent am Besten geeignet. In vielen, einfachen Fällen kann man auch einfach TThread.Synchronize oder TThread.Queue nehmen.
Was mir generell noch fehlt in Lazarus, ist ein Modell, eine Kontroll-Struktur, eine massive
automatisierte Unterstützung von Multi-Threads, in / durch Lazarus.
Also quasi unter "Erzeuge neues Projekt" ein Template "Multi-Threaded Application".
Es gibt aber eben nicht die eine "Multi-Threaded Application". Was im einen Anwendungsfall funktioniert kann im anderen komplett Banane sein. Hast du ja selbst geschrieben:
PeterS hat geschrieben:
Mi 24. Feb 2021, 12:14
Sowas gibt es nicht, und auch meine bisherigen Erfahrungen mit Threads zeigen,
daß dieser fromme Wunsch wohl schwierig zu realisieren ist. Zu unterschiedlich sind wohl
die Arten, wie man Threads nutzt, mal einmalig ("One-Shot"), mal permanent im Hintergrund,
aber nicht zeitkritisch, mal - wie bei Audio und MIDI - mit der Anforderung "Echtzeit", bitte ruckelfrei.
Die Buffer müssen immer voll sein, und "sofort" raus an die Treiber, sonst stottert es.
TThread.Synchronize ist da wohl ungünstig weil es den Thread anhält.
Der Hauptanwendungsfall für Synchronize und Queue ist, dass du etwas in der GUI aktualisieren möchtest. Wenn du etwas in „Echtzeit” haben möchtest, dann sollte der Berechnungsteil eh von der UI entkoppelt sein und diese über eine FIFO oder ähnliches über Aktualisierungen informiert werden.
FPC Compiler Entwickler

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

PascalDragon hat geschrieben:
Do 25. Feb 2021, 09:11
Graeme's Code ist hier verfügbar.
Ups ! Den Link zum Source-Code hab ich garnicht gesehen :o

Danke !
PascalDragon hat geschrieben:
Do 25. Feb 2021, 09:11
PeterS hat geschrieben:
Mi 24. Feb 2021, 12:14
Was mir generell noch fehlt in Lazarus, ist ein Modell, eine Kontroll-Struktur, eine massive
automatisierte Unterstützung von Multi-Threads, in / durch Lazarus.
Also quasi unter "Erzeuge neues Projekt" ein Template "Multi-Threaded Application".
Es gibt aber eben nicht die eine "Multi-Threaded Application". Was im einen Anwendungsfall funktioniert kann im anderen komplett Banane sein. Hast du ja selbst geschrieben: ...
Ich weiß, ich weiß ..

Anscheinend braucht es aber doch zwischen Threads in vielen Fällen ähnliches wie
PostMessage() - im einfachsten Fall - oder irgendein FiFo-Konstrukt ... ?!?

Bei meinem ersten Versuch, vor vielen Jahren, hab ich mir da was zusammen-gestoppelt,
was ich nie ganz verstanden habe .. aber es läuft. :roll:

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

af0815 hat geschrieben:
Do 25. Feb 2021, 06:51
Wenn du über abstrakte Klassen nachdenkst, bist gleich wieder mal beim Interface. :shock:
.. ist eine steile Lernkurve ..
Die Lernkurve, da bin ich gerade noch ganz unten, am Anfang ..

Denn den Zusammenhang [abstrakte Klassen] [Interface] sehe ich - vor lauter Bäumen .. - noch nicht.

Muß mich jetzt am Wochenende damit befassen, und fleißig lesen und lernen.

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PascalDragon »

PeterS hat geschrieben:
Do 25. Feb 2021, 13:13
Denn den Zusammenhang sehe ich - vor lauter Bäumen .. - noch nicht.
Abstrakte Klassen und Interfaces können in erster Näherung als gleichbedeutend angesehen werden (in C++ sind COM Interfaces einfach nur Klassen mit nur abstrakten Methoden).

Code: Alles auswählen

type
  TMyClass = class abstract
    procedure Test; virtual; abstract;
  end;
  
  IMyIntf = interface
     procedure Test;
  end;
Ob meine Klasse nun von TMyClass erbt oder IMyIntf implementiert ist erstmal egal. In beiden Fällen kann ich über TMyClass bzw. IMyIntf eine Test-Methode nutzen.

Die Unterschiede zwischen abstrakten Klassen und Interfaces kommen dann zu Tage, wenn es komplexer wird: eine Klasse kann nur von einer einzigen Klasse ableiten, das heißt du kannst nicht von mehreren abstrakten Klassen erben. Interfaces kannst du wiederum mehrere implementieren. Abstrakte Klassen wiederum müssen nicht vollständig abstrakt sein, sondern können auch Felder oder nicht-abstrakte Methoden enthalten, Interfaces wiederum bieten nur Methoden.
FPC Compiler Entwickler

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

PascalDragon hat geschrieben:
Do 25. Feb 2021, 09:11
Graeme's Code ist hier verfügbar.
Okay. Dann fange ich am Wochenende mal mit MGM an.

MVC scheint ja "noch einen draufzusetzen", und mehr Komplexität
kann ich gerade nicht unbedingt gebrauchen.
Kann ich ja in einem zweiten Schritt angehen, vielleicht zusammen mit ..
PascalDragon hat geschrieben:
Do 25. Feb 2021, 13:20
Abstrakte Klassen und Interfaces ..
da les ich mich auch noch ein ..
.
.
PascalDragon hat geschrieben:
Do 25. Feb 2021, 09:11
Es gibt aber eben nicht die eine "Multi-Threaded Application". Was im einen Anwendungsfall funktioniert kann im anderen komplett Banane sein. Hast du ja selbst geschrieben: ..
Trotzdem bin ich anscheinend nicht der einzige, der sich eine gewisse Vereinfachung
in Sachen Threads wünscht .. resp. einen standarisierten "Message-Kanal" ..
=> https://wiki.lazarus.freepascal.org/ThreadEvent

Die meisten Einträge/Änderungen dort waren allerdings in 2007 und 2011,
seitdem ist der Beitrag fast unberührt am schlafen ..


In wieweit das realisierbar ist, kann ich nicht beurteilen.
Ich hab mal nach meiner FiFo Lösung von damals gesucht,
und der FiFo war ein Ringbuffer, also eine endliche

Code: Alles auswählen

array of TLMessage
Höchst gefährlich weil bei höherem Nachrichtenaufkommen wäre sie überrannt worden ..
Aber PLAY, STOP, RESET, LOAD_FILE etc. .. viel mehr "Messages" waren es nicht, an den Thread.
Und auch in der Gegenrichtung, zum GUI, ist der zuständige FiFo nie überrannt worden ..

PascalDragon
Beiträge: 825
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PascalDragon »

PeterS hat geschrieben:
Do 25. Feb 2021, 16:08
PascalDragon hat geschrieben:
Do 25. Feb 2021, 09:11
Es gibt aber eben nicht die eine "Multi-Threaded Application". Was im einen Anwendungsfall funktioniert kann im anderen komplett Banane sein. Hast du ja selbst geschrieben: ..
Trotzdem bin ich anscheinend nicht der einzige, der sich eine gewisse Vereinfachung
in Sachen Threads wünscht .. resp. einen standarisierten "Message-Kanal" ..
=> https://wiki.lazarus.freepascal.org/ThreadEvent

Die meisten Einträge/Änderungen dort waren allerdings in 2007 und 2011,
seitdem ist der Beitrag fast unberührt am schlafen ..


In wieweit das realisierbar ist, kann ich nicht beurteilen.
Ich hab mal nach meiner FiFo Lösung von damals gesucht,
und der FiFo war ein Ringbuffer, also eine endliche

Code: Alles auswählen

array of TLMessage
Höchst gefährlich weil bei höherem Nachrichtenaufkommen wäre sie überrannt worden ..
Aber PLAY, STOP, RESET, LOAD_FILE etc. .. viel mehr "Messages" waren es nicht, an den Thread.
Und auch in der Gegenrichtung, zum GUI, ist der zuständige FiFo nie überrannt worden ..
Das Beste wäre letztlich, wenn wir die OmniThreadLibrary unterstützen würden. Die bringt viel Bibliothekscode zur vereinfachten Verwendung von Threads mit. Sie benötigt allerdings einige Funktionalitäten, die FPC noch nicht unterstützt. Die sind in Arbeit, aber das dauert leider noch etwas...
FPC Compiler Entwickler

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

PascalDragon hat geschrieben:
Fr 26. Feb 2021, 09:03
Das Beste wäre letztlich, wenn wir die OmniThreadLibrary unterstützen würden. Die bringt viel Bibliothekscode zur vereinfachten Verwendung von Threads mit. Sie benötigt allerdings einige Funktionalitäten, die FPC noch nicht unterstützt. Die sind in Arbeit, aber das dauert leider noch etwas...
Gut, Hoffnung am Horizont :)

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

Model-GUI-Mediator habe ich jetzt schonmal erfolgreich von der fpgui- in die LCL-Welt geholt,
das Demo-Programm läuft jetzt mit Standard-LCL-Komponenten, also TButton, TEdit etc. ..... :D

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von kupferstecher »

Nochmal zur Ausgangsfrage, wie schon einige geschrieben haben, ist das eher eine Frage der Architektur, und weniger der sprachlichen Möglichkeiten. Solange man selber keinen konkreten Nutzen, bspw. in Interfaces, sieht, sollte man sie m.E. auch nicht benutzen. Der Code wird durch diese sprachlichen Möglichkeiten nämlich (so meine ich) nicht automatisch besser, sondern sie helfen einfach bei bestimmten Architekturen. Aber wenn man die nicht nutzt...

Selber habe ich auch Schwierigkeiten, meine Programme zu entflechten. Meine Vorgehensweise ist, so viel vom Programm wie möglich in Units zu packen und diese, das ist das Wesentliche, als Bibliotheken zu verstehen. Da kommt dann die OOP und Kapselung ins Spiel. Die Vererbung braucht es dazu gar nicht. Ich denk das geht in Richtung Komponenten, mir fehlt da aber die Theorie dazu. Auf deine Audio-Engine bezogen heißt das, du machst eine Klasse für den Audiostream. Es kommt dann auf die Anwendung drauf an, auf welche Art und Weise Daten übergeben werden, als komplettes Datenarray, als Stream oder häppchenweise zur Verarbeitung in Echtzeit. Als Bibliothek heißt, du kannst aus dieser Unit/Klasse heraus nie auf deine GUI-Units oder andere direkt zugreifen. Zu Programmstart wird die "Bibliothek" initialisiert, instantiiert, wie auch immer sie aufgebaut ist, Einstellungen und Daten müssen übergeben werden. Zum Teil zum Programmstart, zum Teil erst bei Verwendung. Und für Events, die wirklich "aus der Reihe" an die GUI bzw. andere Programmteile übermittelt werden müssen, werden Callback-Handler registriert. D.h. die "Bibliotheks"-Unit hat einen "procedural type" (=Funktionspointer), dem man bei der Initialisierung einen Handler übergibt. z.B. AudioStream.OnFFTFinished:= @Form1.FFTFinishedHandler. Diese Kapselung führt dann auch dazu, dass Daten mehrfach vorgehalten werden. Bspw. hat die GUI ein TEdit für eine IP-Adresse, die IP wird bei Programmstart und nach Änderungen an die Webserver-Unit übergeben, die eine Variable für die IP führt, diese übergibt die IP an an die Netzwerkkomponente, die wieder die IP lokal führt. Alle Programmteile brauchen die Daten und zu unterschiedlichen Zeitpunkten, dürfen aber nicht alle auf den gleichen Speicherplatz zugreifen, um Verflechtungen zu vermeiden. Hier siehst du auch gleich eine zweite Vorgehensweise, die Unit sollte keine Monsterunit werden, die alle Funktionalität - wieder verflochten - in sich vereint, sondern so viel wie möglich in weitere "Bibliotheken" auslagern. Diese Abhängigkeiten sind dann idealerweise linear, d.h. dass nicht mehrere Units von der gleichen abhängen, oder gar im Kreis herum. Genauso wie Daten auf mehreren Ebenen redundant vorgehalten werden, muss dann ein Callback unter Umständen über mehrere Ebenen weitergeleitet werden. Die Audiostream-Unit wird dann die FFT in einer FFT-Unit berechnen lassen, die wiederum dein Format auf das Format einer Drittbibliothek umbaut. Die Abtastrate wäre dann wieder ein Beispiel, für eine Variable, die über jede Ebene durchgereicht werden muss.

PeterS
Beiträge: 34
Registriert: So 22. Feb 2015, 11:36
OS, Lazarus, FPC: 2.0.10
CPU-Target: win32

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von PeterS »

kupferstecher hat geschrieben:
Sa 27. Feb 2021, 00:33
Nochmal zur Ausgangsfrage, wie schon einige geschrieben haben, ist das eher eine Frage der Architektur, und weniger der sprachlichen Möglichkeiten. Solange man selber keinen konkreten Nutzen, bspw. in Interfaces, sieht, sollte man sie m.E. auch nicht benutzen. Der Code wird durch diese sprachlichen Möglichkeiten nämlich (so meine ich) nicht automatisch besser, sondern sie helfen einfach bei bestimmten Architekturen. Aber wenn man die nicht nutzt ...
Ich hatte gestern einen längeren Beitrag geschrieben, hab das meiste aber
wieder rausgelöscht, weil das noch nicht Hand und Fuß hatte.

Natürlich scheue ich mich davor, Konstrukte zu verwenden, die ich (noch) nicht ganz durchblicke (=> Interfaces, abstrakte Klassen)
Aber, das war mein Eindruck nach fleißigem lesen gestern, ich habe möglicherweise
das Paradigma "benutze keine globalen Variablen" falsch verstanden.
https://stackoverflow.com/questions/577 ... hi/5777482
Möglichweise gilt das nur für "einfache Datentypen", nicht aber unbedingt für TObjects ?

Es gibt ja gute Gründe, nicht alles offen zu legen, (siehe den Link..) aber jedes neu angelegte LCL Formular
hat ja - automatisch angelegt - eine globale Variable var MyForm: TForm; in der INTERFACE Sektion der Unit.

Wenn ich jetzt zu meinem Beispiel zurückkehre, ich hatte - um sie nicht als globale Variablen zu definieren (=> Paradigma) -
entweder "TAudioEngine" als var in den private-Abschnitt von "TAudioFiles" gehängt, oder umgekehrt.
In meinen jeweiligen Units und auch in der Unit der MainForm gab es nie eine
globale Variable "var MyAudioEngine: TAudioEngine" oder "var MyAudioFiles: TAudioFiles"

Wenn ich das aber jetzt so überschaue, spricht doch eigentlich nichts dagegen,
"TAudioEngine" als globale Variable anzulegen.
(hier: Highlander-Prinzip, von dieser Art meiner Objekte darf es in einem Programm niemals mehr als 1 Objekt geben)
Bei "TAudioFiles" sieht das schon anders aus, öffnet man zwei Projekte zugleich, braucht man zwei verschiedene Sätze AudioFiles ..

Im dollsten Falle müßte man "TAudioEngine" als globale Var sogar
in der INITIALIZATION section der Unit starten
und in der FINALIZATION section destroyen können ... dürfen ... denke ich ... ???
kupferstecher hat geschrieben:
Sa 27. Feb 2021, 00:33
Zu Programmstart wird die "Bibliothek" initialisiert, instantiiert, wie auch immer sie aufgebaut ist, Einstellungen und Daten müssen übergeben werden. Zum Teil zum Programmstart, zum Teil erst bei Verwendung. Und für Events, die wirklich "aus der Reihe" an die GUI bzw. andere Programmteile übermittelt werden müssen, werden Callback-Handler registriert. D.h. die "Bibliotheks"-Unit hat einen "procedural type" (=Funktionspointer), dem man bei der Initialisierung einen Handler übergibt. z.B. AudioStream.OnFFTFinished:= @Form1.FFTFinishedHandler.
Dein ausführlich beschriebener Ansatz mit den Events / Callback-Handlern
kam bei mir noch nie zum Einsatz, das sollte ich mal probieren !

Das Problem ist halt daß ich damals, zu Delphi 5 (.. 6, .. 7)-Zeiten, noch alles recht störungsfrei im MainThread anlegen konnte.
Unter dem LCL-Konstrukt stottert eine MIDI- oder Audio-Ausgabe manchmal bereits,
wenn man bloß ein ShowMessage('blablabla') öffnet. :(
Dagegen hilft nichtmal ein HighRes-Timer ..
MIDI und Audio hab ich dann unter der LCL in Threads abgeschoben, was das debuggen nicht gerade erleichtert ..

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: objektorientierte Programmierung - komponentenbasierte Programmierung

Beitrag von af0815 »

Das mit der TForm ist der IDE geschuldet. Stelle das einmal in den Projektoption um, das eine neue Form nicht automatisch erzeugt wird. Dann sollte die Variable nicht automatisch bei einem neuen Formular erzeugt werden.

Global ist nicht böse, nur sollte man damit nicht eine unnötige Kopplung erzwingen. Ich mache mir gerne ein Objekt, das ein jeder der es braucht bekommen kann, so können unabhängige Fenster auf eine gemeinsame Datenstruktur zugreifen. Hat aber auch damit zu tun, das ich sehr viel mit Frames und vererbten Frames, Forms etc. arbeite. Damit habe ich ein Baukastensystem. Das habe ich bei tiOpf gesehen und gelernt wie man das verwendet.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten