Hallo Freunde der Bits und Bytes,
ich bin gerade dabei meine erste "richtige" Windows Anwendung zu
programmieren. In C++ bin ich in der Zwischenzeit relativ Sattelfest
(würde ich behaupten). Jetzt ist aber die Frage, wie ich meine Anwendung
aufbaue.
Also konkret:
Ich habe eine Application Klasse/Objekt als Basis. Dieses
erstellt/öffnet unter anderem das Hauptfenster. Dieses
Hauptfenster-Objekt kümmert sich fortan um die ganze Interaktion mit dem
Benutzer und ist im Prinzip "self-contained" ;) . Nun möchte man
natürlich über die GUI auch irgendwelche Berechnungen anstossen oder
Daten abrufen. Jetzt ist die Frage, wie kommt die GUI konkret an diese
Daten? Ich hab es jetzt mal so gelöst, dass ich eine Referenz zu dem
Application Objekt an das Hauptfenster übergeben habe. Dieses gibt diese
Referenz bei Bedarf an andere Fensterobjekte weiter. So kann jedes
Fenster auf das oberste Objekt der Anwendung zugreifen. Ich habe auch
schon die Lösung gesehen, mit einer globalen getApplication() Funktion,
aber das sagt mir irgendwie nicht so zu. Ist wahrscheinlich
Geschmacksache.
Das eigentliche Problem folgt jetzt. Wie weiter? Ich kann jetzt
natürlich über
auf alles mögliche zugreifen, aber das finde ich alles andere als
Wartungsfreundlich und Übersichtlich. (Vorallem für mich, als Anfänger,
bei dem das Backend immer mal wieder umgeschrieben wird, wenn ich eine
bessere Struktur finde). Ausserdem muss die GUI sich auch um Sachen wie
Mutex und ähnlich kümmern, was meiner Meinung nach nicht so toll ist.
Irgendwie bräuchte ich eine "Schicht" dazwischen.
Da gäbe es das Command Pattern, aber das funktioniert so wie ich das
verstehe nur unidirektional. Selbst wenn ich das doppelt (einmal
GUI->backend, einmal Backend->GUI) machen würde - wenn sich z.B. ein
Wert im Backend 10000mal pro Sekunde ändert, ich ihn aber nur z.B. 10mal
pro Sekunde anzeigen möchte, quillt die FIFO im nu über... Wie seht Ihr
das? Was hat sich für euch in der Praxis bewährt? Gerne auch Links oder
Buchempfehlungen (am liebsten Deutsch.)
Vielen Dank für Eure Kommentare.
Rasputin schrieb:> Also konkret:> Ich habe eine Application Klasse/Objekt als Basis. Dieses> erstellt/öffnet unter anderem das Hauptfenster. Dieses> Hauptfenster-Objekt kümmert sich fortan um die ganze Interaktion mit dem> Benutzer und ist im Prinzip "self-contained" ;) . Nun möchte man> natürlich über die GUI auch irgendwelche Berechnungen anstossen oder> Daten abrufen. Jetzt ist die Frage, wie kommt die GUI konkret an diese> Daten? Ich hab es jetzt mal so gelöst, dass ich eine Referenz zu dem> Application Objekt an das Hauptfenster übergeben habe. Dieses gibt diese> Referenz bei Bedarf an andere Fensterobjekte weiter. So kann jedes> Fenster auf das oberste Objekt der Anwendung zugreifen.
Das ist kein schönes Design.
> Ich habe auch schon die Lösung gesehen, mit einer globalen> getApplication() Funktion, aber das sagt mir irgendwie nicht so zu.
Es hat das gleiche Problem: Alle deine Klassen, an die du dein
Application-Objekt weitergibst, haben damit eine Abhängigkeit von
Application, selbst wenn sie nur einen Bruchteil davon nutzen. Wenn man
das ganze flexibel halten will, ist es gut, Abhängigkeiten, die nicht
unbedingt nötig sind, zu vermeiden.
Da stimme ich Dir zu. Aber mir fehlt der richtige Ansatz. Ich bräuchte
sowas wie ein "application abstraction layer", aber diese Bezeichnung
scheints nicht zu geben. Hat jemand ein Google tauglicher Suchbegriff
für mich?
Daniel F. schrieb:> 'MVC Pattern'
Kenn' ich. Aber aus meiner Sicht ist das Window Objekt View.
Application ist Controller und der ganze (Backend-)Rest Model. Gut,
so gesehen müsste ich einfach die Application-Klasse mit
"Proxy-Methoden" füllen, und deren restlichen Inhalt private
klassifizieren. Wäre das die richtige Vorgehensweise? Das wird doch auch
irgendwann unübersichtlich, nicht?
Hallo,
das Application Objekt zu übergeben ist nicht unüblich und muss auch
keine Abhängigkeit schaffen. Du definierst ein Protokoll in Form einer
abstrakten Klasse. Du leitest die Application Klasse von dem Protokoll
ab und implentierst die vorgesehenen Funktionen. Das Kind akzeptiert die
Übergabe eines Objektes, dass das Protokoll implementiert. Ob Du jetzt
das Protokoll in einem eigenständigen Objekt realisierst, oder einfach
das Application Objekt benutzt, hängt von den Umständen ab, es ist
definitiv nicht unüblich das Application Objekt zu benutzen, die
Abhängigkeiten sind über das Protokoll wohl definiert und es gibt keine
Abhängigkeit von der Application Klasse.
Den Fall, dass ein Kindeskind mit der Eltern-Klasse kommunizieren muss
ist da schon eher etwas ungewöhnlich, normalerweise würde sich doch eine
natürliche Aufteilung ergeben, bei der immer der direkte Elternteil die
Ergebnisse des Kindes weiterverarbeitet und dann weiterleitet. Wenn Du
zum Beispiel zwei Werte a und b ermitteln möchtest, würde ja
normalerweise das Application Objekt ein AB-Ermittler Objekt damit
beauftragen (oder ein A-Ermittler und ein B-Ermittler Objekt). Ob jetzt
der AB-Ermittler a selbst ermittelt oder dafür wieder eine andere
Struktur anstößt, kann dem Application Objekt eigentlich doch schnurz
sein und dem A-Ermittler auch, der liefert an den AB-Ermittler.
Deine Idee mit dem global zur Verfügung gestellten Objekt ist aber auch
nicht unüblich, es gibt in verschiedenen Frameworks, zum Beispiel auch
beim auf MacOS dominierenden Cocoa Funktionen um das Application Object
zu bekommen (dort App Delegate). Du solltest aber dann eben aufpassen,
dass die Abhängigkeiten wohl definiert sind, zB über Protokolle oder
Interfaces aka abstrakte Klassen.
Wenn es eine Reihe von Werten gibt, die für mehrere Klassen / Objekte
relevant sind, könntest Du auch einen globalen Speicher dafür verwenden,
über den man Werte setzen und lesen kann und bei dem sich Klassen als
Observer für bestimmte Werte registrieren können, um bei Änderungen
benachrichtigt zu werden.
Vlg
Timm
Wie erzeugst Du die GUI? QT? WIN32? MFC? Du solltest nicht gegen die
Philosophie des Frameworks arbeiten.
Zudem: von Anfang an 2 threads. Einen fuer die GUI einen fuer den Rest.
Wenn eine Berechnung laenger dauert ist eine blockierte GUI
unprofessionell.
Dumdi D. schrieb:> Zudem: von Anfang an 2 threads. Einen fuer die GUI einen fuer den Rest.> Wenn eine Berechnung laenger dauert ist eine blockierte GUI> unprofessionell.
Sehe ich nicht so. Es macht nur unnötig Umstände, alles zwischen den
Threads austauschen zu müssen. Wenn man was hat, was länger dauern kann,
dann lagert man das in einen eigenen Thread aus. Prophylaktisch
Threads anzulegen, halte ich nicht für sinnvoll.
Fred Brooks schrieb vor 40 Jahren:
The management question, therefore, is not whether to build a pilot
system and throw it away. You will do that. […] Hence plan to throw one
away; you will, anyhow.
Ich habe noch von keiner einzigen "richtigen" Anwendung gehört, das
Brooks Einschätzung widerlegt hätte,
Noch einer schrieb:> The management question, therefore, is not whether to build a pilot> system and throw it away. You will do that. […] Hence plan to throw one> away; you will, anyhow.
Kannte ich nicht, hört sich aber gut an. :-)
Nur mal eine Anmerkung zu Diskussionen zu C++ (ich habe ein wirklich
dickes Buch hier, trau mich aber nach all dieser Diskussionen das nicht
anzufangen):
Wenn ich diese Beiträge verfolge (und die lese ich sehr aufmerksam),
dann habe ich das Gefühl, dass hier die Leute ganze Universen
auseinander liegen.
Ich dachte immer, C++ soll es einfacher machen. Gerade im Bezug auf
Zusammenarbeit. Was ich hier bisher las (nicht in diesem Thread),
erweckt bei mir eher den Verdacht, dass da jegliche Zusammenarbeit nur
möglich ist, wenn einer oben drüber steht und heftig mit der Peitsche zu
haut, sollte jemand eine eigenmächtige Idee entwickeln.
Rolf M. schrieb:> Wenn man was hat, was länger dauern kann,> dann lagert man das in einen eigenen Thread aus.
Wenn man nichts dergleichen hat, braucht man auch keine Anwendung zu
schreiben. Alles kann länger dauern, insbesondere das, wo man denkt, es
sei schnell.
Dateizugriffe? Ooops, nicht an Netzlaufwerke gedacht. Netzwerkzugriffe?
Ooops, nicht ans Internet gedacht. Oder auch beides zusammen bei Einwahl
mit VPN.
Der GUI-Thread ist sowas wie die Interruptroutine der Anwendungswelt. Da
macht man nichts drin, sondern setzt nur Signale und Daten auf.
Rasputin schrieb:> sowas wie ein "application abstraction layer"
Einfach nach den Stichworten factory manager controller googeln. Oder
auch gleich mit dieser netten Satire dazu anfangen:
http://discuss.joelonsoftware.com/default.asp?joel.3.219431.12
Daß OOP übrigens ein erhöhtes Maß an Wiederverwendbarkeit brächte, ist
ein Märchen. Was es bringt, sofern es um GUI-Programmierung oder
Simulationen geht (da kommt OOP ja her), ist mehr Übersichtlichkeit.
F. F. schrieb:> Ich dachte immer, C++ soll es einfacher machen. Gerade im Bezug auf> Zusammenarbeit. Was ich hier bisher las (nicht in diesem Thread),> erweckt bei mir eher den Verdacht, dass da jegliche Zusammenarbeit nur> möglich ist, wenn einer oben drüber steht und heftig mit der Peitsche zu> haut, sollte jemand eine eigenmächtige Idee entwickeln.
Das hat mit C++ im speziellen nichts zu tun. Das gilt für so ziemlich
jede Art von Zusamnmenarbeit beim Programmieren. Da kann man sich nun
mal nicht einfach so austoben und machen, was einem gerade in den Sinn
kommt.
Es muss einfach Guidelines geben, damit das am Ende aus einem Guss ist
und nicht wie Kraut und Rüben aussieht.
nicht"Gast" schrieb:> Das hat mit C++ im speziellen nichts zu tun.
Doch, weil das mit C++ aufgrund des Umfangs besonders ausgeprägt ist.
Deswegen wird normalerweise Subsetting gemacht, damit das überhaupt noch
beherrschbar bleibt, insbesondere bei Personalfluktuation.
Ansonsten fügt jeder Programmierer irgendwelche coolen Features ein, nur
weil er die zufällig mal kennt und drauf steht, und nach 5 Jahren hat
man dann ein Gesamtkunstwerk, was kein Wartungsprogrammierer mehr
durchschaut.
Ist ja selbst bei C schon so, obwohl die Sprache viel kompakter ist
(MISRA-C sei genannt).
Nop schrieb:> Doch, weil das mit C++ aufgrund des Umfangs besonders ausgeprägt ist.> Deswegen wird normalerweise Subsetting gemacht, damit das überhaupt noch> beherrschbar bleibt, insbesondere bei Personalfluktuation.
Hä?
C++ als Sprachumpfang empfinde ich jetzt mal nicht als allzu
umfangreich. Vergleiche das mal mit den Features, die zum Bsp. C#
bieten. (Damit meine ich nicht das .Net Framework, sondern die Features,
welche die Sprache selbst anbietet wie LINQ, Events, Extension Methods,
Stringformatierung mit $"", Nullprüfung beim dereferenzieren also.
Object?.Foo())
C++ ist naturgegeben umfangreicher als C aber so übertrieben nun auch
wieder nicht.
nicht"Gast" schrieb:> C++ als Sprachumpfang empfinde ich jetzt mal nicht als allzu> umfangreich.
Rechne in der Praxis einfach noch STL/Boost dazu, denn
Anwendungsprogrammierung wird man ja wohl nicht mit einem reinen
C++-Compiler machen.
F. F. schrieb:> Nur mal eine Anmerkung zu Diskussionen zu C++ (ich habe ein wirklich> dickes Buch hier, trau mich aber nach all dieser Diskussionen das nicht> anzufangen):>> Wenn ich diese Beiträge verfolge (und die lese ich sehr aufmerksam),> dann habe ich das Gefühl, dass hier die Leute ganze Universen> auseinander liegen.
Ja. Das liegt speziell hier in diesem Forum daran, daß es hier primär um
Mikrocontroller geht, also um überwiegend kleine Projekte mit einer eher
überschaubaren Anzahl von LOC, bei denen das Applikationsdesign und auch
die Pflege und Erweiterung von bestehendem Code oft eine untergeordnete
Rolle spielen. Viele Mikrocontroller-Spezialisten haben noch nie eine
Anwendung > 100 kLOC entwickelt und gepflegt. (Bitte nicht mißverstehen:
das ist weder Häme noch Vorwurf, sondern nur eine sachliche
Feststellung. Embedded-Entwickler stehen vor ganz anderen, keineswegs
weniger komplexen Herausforderungen, und haben darum natürlich einen
anderen Fokus.)
> Ich dachte immer, C++ soll es einfacher machen. Gerade im Bezug auf> Zusammenarbeit. Was ich hier bisher las (nicht in diesem Thread),> erweckt bei mir eher den Verdacht, dass da jegliche Zusammenarbeit nur> möglich ist, wenn einer oben drüber steht und heftig mit der Peitsche zu> haut, sollte jemand eine eigenmächtige Idee entwickeln.
Embedded-Umgebungen unterscheiden sich aber nicht nur bei der Größe,
sondern auch in anderen Punkten deutlich von üblichen Applikationen:
üblicherweise ist die Hardware begrenzt, so daß neue Funktionen kaum
oder gar nicht eingebaut werden können, und obendrein ist die Hardware
häufig schwer zu erreichen und der darauf vorhandene Code deswegen
schwierig zu ändern. Gerade in der Organisation, Pflege und Erweiterung
von Code liegen aber die besonderen Stärken der Objektorientierung und
damit auch jene von C++, und gerade diese Bereiche sind im
Embedded-Umfeld wesentlich weniger wichtig als beim herkömmlichen
Applikationsdesign.
Das ist auch der Grund dafür, warum das Embedded-Umfeld einer der sehr
wenigen Bereiche ist, in dem noch über den Sinn und Zweck von höheren
Programmiersprachen und modernen Techniken wie der Objektorientierung
gestritten wird. Und es ist gleichzeitig der Grund dafür, warum das
Fachwissen über objektorientiertes Design nicht so ausgeprägt ist wie,
sagen wir, bei Entwicklern grafischer Businessapplikationen.
Mit Deinem C++-Buch kannst Du jetzt drei Dinge tun: Du kannst es einfach
in Deinem Regal stehen lassen, oder es verschenken. Wenn Du es liest,
wirst Du jedoch auch dann etwas lernen und mitnehmen, wenn Du Dich
hinterher dagegen entscheidest, C++ zu verwenden. Viel Spaß!
Nop schrieb:> Rolf M. schrieb:>> Wenn man was hat, was länger dauern kann,>> dann lagert man das in einen eigenen Thread aus.>> Dateizugriffe? Ooops, nicht an Netzlaufwerke gedacht. Netzwerkzugriffe?> Ooops, nicht ans Internet gedacht. Oder auch beides zusammen bei Einwahl> mit VPN.
Zugriffe auf externe Ressourcen gehören fast immer in einen eigenen
Thread. Dazu muß man aber nicht die ganze Applikation in einzelne
Threads verpacken, sondern kann das Threading auf diese Art von
Operationen beschränken und die Threads entsprechend nur bei Bedarf
starten.
> Der GUI-Thread ist sowas wie die Interruptroutine der Anwendungswelt. Da> macht man nichts drin, sondern setzt nur Signale und Daten auf.
Einen Großteil ihrer Kommunikation macht eine GUI mit sich selbst, und
dazu braucht normalerweise es kein Multithreading.
Sheeva P. schrieb:> Gerade in der Organisation, Pflege und Erweiterung> von Code liegen aber die besonderen Stärken der Objektorientierung und> damit auch jene von C++, und gerade diese Bereiche sind im> Embedded-Umfeld wesentlich weniger wichtig als beim herkömmlichen> Applikationsdesign.
Auch Embedded Software ist oftmals seit Jahrzehnten im Einsatz und wird
ständig weiterentwickelt.
> Das ist auch der Grund dafür, warum das Embedded-Umfeld einer der sehr> wenigen Bereiche ist, in dem noch über den Sinn und Zweck von höheren> Programmiersprachen und modernen Techniken wie der Objektorientierung> gestritten wird.
Bei Embedded geht es meistens um reale Objekte. Zylinder, Räder,
Motoren, Sensoren, etc. Und hier um konkrete Instanzen, z.B. um genau
die Räder VL, VR, HL, HR. Die Software ist (natürlich) auch in C
objektorientiert gestaltet. Bei konkreten Objekten halten sich der
Vorteil der OOP-Sprachen in Grenzen. Beispielsweise gibt es meist keinen
Sinn, das Rad VL mit new zu erzeugen oder von einer virtuellen Klasse
"Rad" abzuleiten.
Bei virtuellen Objekten ist die OOP dagegen vorteilhaft. Z.B.
gezeichnete Bauteile in einem CAD-Programm, Daten in einem
Abrechnungssystem, GUI-Elementen, ...
Der Vorteil von C++ in der embedded Programmierung liegt m.E. eher in
Templates und besserer Optimierung. Leider bläst man sich manchmal auch
sein ganzes Bein damit weg, während MISRA und Co den Fuß relativ gut
schützen. Es hat wohl seinen Grund, warum AUTOSAR (als beispiel) C
favorisiert.
Sheeva P. schrieb:> Mit Deinem C++-Buch kannst Du jetzt drei Dinge tun: Du kannst es einfach> in Deinem Regal stehen lassen, oder es verschenken. Wenn Du es liest,> wirst Du jedoch auch dann etwas lernen und mitnehmen, wenn Du Dich> hinterher dagegen entscheidest, C++ zu verwenden. Viel Spaß!
Ich fand die Sprache von Anfang an sehr verständlich und einleuchtend,
doch entschied ich mich eher C zu lernen, weil das nun einmal mehr auf
uC verwendet wird.
Daniel F. schrieb:> Suche z.b. nach 'MVC Pattern'
Das MVC Pattern ist schon ziemlich "alt", es lässt sich auch nicht in
allen Sprachen sauber integrieren.
Es gibt mittlerweilen ein gefühltes halb Dutzend Alternativen dazu. MVC
macht keinen Sinn, wenn eine Maske nur eine Funktionalität erfüllt. MVC
basiert darauf, das Werte aus verschieden Masken übernommen und
aktualisiert werden müssen.
Patterns sind manchmal Hilfreich, lösen aber nicht jedes Problem.
Achim S. schrieb:> Bei Embedded geht es meistens ...
Es geht aber um Windows, was kommt ihr jetzt wieder auf die ewige C++
Diskussion im Embedded Bereich ab.
Grüsse,
René
Objektorientierung ist eine nette Idee, funktioniert in der Praxis aber
offensichtlich nicht. Man dürfte sich schwer damit tun, Applications
ausfindig zu machen, die die strikt hierarchische Gliederung, die für
Objektorientierung eigentlich grundlegend ist, von A bis Z durchhalten.
Und da die dann notwendig geknickt wird, hat man es eben NICHT mehr mit
einzelnen, für sich stehenden "Objekten" zu tun, sondern zupft mit jedem
einzelnen Funktionsaufruf potentiell an einem wild verwachsenen
Gestrüpp, wo, ganz nach Chaos-Prinzip, alles alles bewirken kann und die
Objektorientierung nur noch dafür sorgt, dass die zusammenhängenden
Code-Teile über möglichst viele Dateien verstreut sind.
Den Irrsinn der Objektorientierung sieht man auch in jedem UML-Diagramm:
Die Relationen zwischen den Klassen definieren deren Rollen in Bezug
aufeinander. Worin also soll der Kern einer Klasse bestehen, wenn sie
doch im ganzen reflektiert durch ihre Rollen definiert sind und dabei
das, was für die eine Rolle völlig unwichtig ist, zusammensteht mit dem,
was ihren unmittelbaren funktionalen Bezug im Konkreten ausmacht?
Das wusste schon Immanuel Kant: Der Objekt-Charakter der Dinge ist
idealer Natur. Scheitern tut's an der Realität - das merken jetzt die
Physiker.
Konkret: das GUI hat GUI zu sein und ist damit genug beschäftigt. Es
macht nicht anderes als Tab und Spaces etc. Richtig zu interpretieren
und schön auszusehen. Da wird nichts gerechnet oder sonst was.
Der Controller leitet bei Aktion alles weiter was nachgeführt wurde
(sowohl an die anderen GUIs als auch an die Models. Der Controller ist
der Chef).
Die Arbeit machen die Models, rechnen, geben es dem Controller zurück,
der schaut das alle aktuell sind.
In C++ musst du schauen, so wenig Abhängigkeiten zu bauen mit wenig
Schnittstelle wie möglich. Eine Pauschallösung gibt es nicht.
Erfahrungen sind da wichtig.
Grüsse,
René
zer0 schrieb:> Objektorientierung ist eine nette Idee, funktioniert in der Praxis aber> offensichtlich nicht. Man dürfte sich schwer damit tun, Applications> ausfindig zu machen, die die strikt hierarchische Gliederung, die für> Objektorientierung eigentlich grundlegend ist, von A bis Z durchhalten.
Stimmt so nicht. OO muss man können, aber auch verstehen. OO in C++ ist
kein C mit mehr Features. OO ist ein anderes Denkmodell, so wie das z.B.
VHDL auch ist.
Grüsse,
René
René H. schrieb:> Stimmt so nicht. OO muss man könnem, aber auch verstehen.
Oh, verständlich ist das. Das macht es aber nicht zu einem adäquaten
Denkmodell.
> OO in C++ ist kein C mit mehr Features.
Genau genommen doch. Ist nur weniger Aufwand, die vftables zu
definieren. Davon abgesehen bringt C++ für den Entwicklungsbetrieb
gerade aufgrund seiner Mächtigkeit viele Fallstricke mit sich.
Ausgeschriebene Funktionsaufrufe z.B. erkennt man auf Anhieb - darauf zu
kommen, dass irgendwer z.B. den "+"-Operator nicht-trivial überladen
hat, kann schon etwas dauern.
> VHDLVHDL ist in vielen Aspekten ein gutes Beispiel für eine ganz schlechte
Verschleierung und Mischung von sprachlichem Ausdruck und resultierender
Schaltung. Wenn man sich beim Durchlesen des Quelltextes schon immer
fragen muss "Was soll das sein?" (heißt: zu was wird das wohl
synthetisiert?) ist viel über die Adäquatheit des Ausdrucks gesagt.
> OO ist ein anderes Denkmodell
Ja - nehmen wir mal ein Beispiel. Vererbungsbaum "Tier -> Insekt". Der
steht so da. Man hat dann noch einen ganzen Sack voll mit
Konkretisierungen, sagen wir mal ein paar Hundert so.
Jetzt kommt nun der Punkt, an dem man nur noch fliegende Dinger
betrachten und denen ein paar "operationale Features" spendieren will.
Das sieht schon einmal ziemlich schlecht aus. Man will wohl kaum an ein
paar Hundert Implementierungen rangehen und da jeweils noch eine
Vererbung deklarieren. Vielleicht kann man das noch nicht einmal, weil
einige Implementierungen proprietärer Quelltext sind. Dabei könnte man
vielleicht die Klassenzugehörigkeit anhand der bekannten Eigenschaften
der Objekte "at runtime" bestimmen. Das aber geht in C++ ganz bestimmt
nicht auf einem deklarativen Level. Man kann zwar einen Wrapper
"Flieger" schreiben, und den über die zutreffenden Objekte stülpen, das
aber wiederum führt das Konzept an sich völlig ad absurdum. Der
"Flieger" IST ja das Objekt. Und schon ist man auf dem Wege nur noch
völligen Unsinn zu fabrizieren.
Und selbst wenn man, wie in Objective-C, da noch Kategorien an die
Objekte anhängen kann, stellt sich doch grundsätzlich die Frage, was
denn gerade die ursprünglichen Eigenschaften des Objekts dazu
qualifizierte, die grundlegenste Definition zu liefern. Vielleicht will
man die Implementierungen ja auch noch an anderer Stelle verwenden und
braucht dann wiederum einige Eigenschaften von denen nicht. Konsequent
durchgeführt dürften also nur vollkommen leere "Objekte" zurückbleiben,
an die man dann, je nach Bedarf, die benötigten Kategorien anheftet. Die
Abhängigkeiten zwischen denen aber kann man soweit ich weiß nicht einmal
in Objective-C ausdrücken -> Pech gehabt. Eigentlich ist es doch klar,
dass alles, was Beine hat und am Leben ist auch laufen kann...
Solche Situationen ergeben sich laufend, wenn sich das
Anforderungsprofil ändert.
zer0 schrieb:> Solche Situationen ergeben sich laufend, wenn sich das> Anforderungsprofil ändert.
Deswegen wissen Softwarearchitekten/-entwickler die ihr Geld wert sind
auch schon seit zig Jahren, dass man Ableitungen sparsam einsetzen
sollte und ob es in so einem Fall nicht auch eine Komposition tut.
Zusammen mit DI und IoC, kann man durchaus gut gekapselte OO-Software
schreiben. Funktioniert erstaunlich gut auf Arbeit.
zer0 schrieb:> Vererbungsbaum "Tier -> Insekt".
Vererbung war mal der ganz heiße Sh*ce in OOP, ist aber schon längst
wieder aus der Mode geraten. U.a. weil es Katzenhunde in der realen Welt
nunmal gibt, und sie werden einen früher oder später kratzen UND beißen.
zer0 schrieb:> Objektorientierung ist eine nette Idee, funktioniert in der Praxis aber> offensichtlich nicht.
Genau, deswegen ist die rein objektorientierte Programmiersprache Java
ja auch die verbreitetste und beliebteste Sprache für die Entwicklung
von umfangreichen Businessapplikationen -- dicht gefolgt von den
ebenfalls objektorientierten Sprachen C++, Python, und C#.
Achim S. schrieb:> Sheeva P. schrieb:>> Gerade in der Organisation, Pflege und Erweiterung>> von Code liegen aber die besonderen Stärken der Objektorientierung und>> damit auch jene von C++, und gerade diese Bereiche sind im>> Embedded-Umfeld wesentlich weniger wichtig als beim herkömmlichen>> Applikationsdesign.>> Auch Embedded Software ist oftmals seit Jahrzehnten im Einsatz und wird> ständig weiterentwickelt.
Hatte ich das igendwie in Abrede gestellt? Im Gegenteil hatte ich sogar
die meist wesentlich längeren Produktionszyklen,
Aktualisierungsintervalle und Nutzungsdauern im Embedded-Umfeld
angedeutet.
Tatsächlich hatte ich mehrere Sätze geschrieben, die miteinander in
einem engen Zusammenhang stehen. Bitte lies meine Ausführungen noch
einmal und versuch dabei, diesen Zusammenhang zu verstehen. Viel Erfolg!
> Bei Embedded geht es meistens um reale Objekte. Zylinder, Räder,> Motoren, Sensoren, etc. Und hier um konkrete Instanzen, z.B. um genau> die Räder VL, VR, HL, HR. Die Software ist (natürlich) auch in C> objektorientiert gestaltet. Bei konkreten Objekten halten sich der> Vorteil der OOP-Sprachen in Grenzen. Beispielsweise gibt es meist keinen> Sinn, das Rad VL mit new zu erzeugen oder von einer virtuellen Klasse> "Rad" abzuleiten.
Objektorientierung erschöpft sich nicht in der Vererbung. In dem
konkreten Beispiel reicht es, eine Rad-Klasse zu erstellen -- denn
letztlich haben die vier Räder eines Fahrzeuges ja durchaus gewisse
Gemeinsamkeiten -- und von dieser Klasse vier Instanzen für VR, VL, HR
und HL zu erzeugen. Und ob diese Instanzen dann auf dem Stack oder auf
dem Heap angelegt werden, ist vollkommen unerheblich und hat auch nichts
mit OO zu tun.
Sheeva P. schrieb:> Genau, deswegen ist die rein objektorientierte Programmiersprache Java> ja auch die verbreitetste und beliebteste Sprache für die Entwicklung> von umfangreichen Businessapplikationen -- dicht gefolgt von den> ebenfalls objektorientierten Sprachen C++, Python, und C#.
Wobei die Softwarequalität in der Entwicklung LÄNGST nicht in dem Maße
zunimmt wie die Leistungsfähigkeit der Hardware. Was sagt uns das? Man
braucht die schnellere Hardware, damit die Apps TROTZ des neuesten
Redmond-Sh*ts noch genau so schnell laufen wie vorher! 2Ghz Quadcore
reicht eben nicht für "Hello, world!"
Abradolf L. schrieb:> zer0 schrieb:>> Solche Situationen ergeben sich laufend, wenn sich das>> Anforderungsprofil ändert.>> Deswegen wissen Softwarearchitekten/-entwickler die ihr Geld wert sind> auch schon seit zig Jahren, dass man Ableitungen sparsam einsetzen> sollte und ob es in so einem Fall nicht auch eine Komposition tut.> Zusammen mit DI und IoC, kann man durchaus gut gekapselte OO-Software> schreiben. Funktioniert erstaunlich gut auf Arbeit.
"Auf Arbeit" hat man eben seinen aufgebohrten Assembler "Version C" oder
den makrolastigeren "C++"! Dass man DAS noch hinbekommt verdankt sich
den Kommandier-Künsten des Programmierers. Frameworks und IoC werden
immer dann interessant, wenn man irgendein in tiefster Tiefe vergrabenes
behaviour aushebeln muss, das den konkreten Use-Case verunmöglicht.
Natürliche Antwort : "Da kann man nichts machen!" ist IoC!
zer0 schrieb:> Redmond-Sh*ts noch genau so schnell laufen wie vorher! 2Ghz Quadcore
Äh, die Welt besteht nicht nur aus Windows. Die OO Welt schon gar nicht.
In unseren Rechenzentren dürfte Windows mit nicht mal 2% vertreten sein
und es sind mehrere Tausend Rechner.
Grüsse,
René
Um noch einmal auszuholen:
Zwischen den Formulierungen
1
typedefstructXx_t;
2
voidxSomeFunction(constx_t*x)
und
1
classX{public:voidsomeFunction()const;};
besteht ein Unterschied von ziemlich genau 0, wenn der Tag vorbei ist.
Beides ist tendenziell SCHLECHT . Mit ein wenig Inhalt gefüllt, wird
das wohl deutlicher. Eine Funktion ist mathematisch definiert als
x(Eingabe)=Ausgabe. Jede in einer Klasse definierte Funktion sowie jede
Funktion, die so eine "Objekt"-Struktur übernimmt, deklariert damit
implizit, dass ihr Ergebnis von sämtlichen Eigenschaften des Objekt
abhinge. Schaut man sich jedoch konkrete Implementierungen an, wird man
in 99.9% der Fälle feststellen, dass man da jede Menge unbenutzte
Eingaben zurück behält. Warum also sollte man diese letztlich
unbenutzten Eingaben extra instanzieren müssen, um das Ergebnis einer
Funktion zu berechnen, die sie gar nicht benutzt? Funktionalität, die
man ggf. noch außerhalb des Kontextes der Klasse benötigen könnte, wird
so - völlig unnötiger Weise - von der Existenz eines passenden Objekts
abhängig gemacht. Oftmals sind solcherlei nützliche Funktionen dann auch
noch "private" deklariert, weil sie für das "öffentliche Interface" der
Klasse, so wie sie der OO-Programmierer zweckbestimmt hat, nicht
relevant sind.
Zig mal gesehen... -- Umbauen, 'public static'/'extern "C"' mit allen
Eingaben als Parameter und dann die eigentliche Funktion aufrufen!
Aha, das hat mit dem Thema genau was zu tun? Wenn du mit OO oder C++ ein
Problem hast, mach bitte einen eigenen Thread. Das gehört nicht zum
Thema vom TO.
Grüsse,
René
Nop schrieb:> Dateizugriffe? Ooops, nicht an Netzlaufwerke gedacht. Netzwerkzugriffe?> Ooops, nicht ans Internet gedacht. Oder auch beides zusammen bei Einwahl> mit VPN.
Nun gut, wenn man auf alles blockierend wartet, kommt man ohne Threads
natürlich nicht weit. Aber nur um auf Daten zu warten, braucht man
meisten nicht gleich einen Thread.
> Der GUI-Thread ist sowas wie die Interruptroutine der Anwendungswelt. Da> macht man nichts drin, sondern setzt nur Signale und Daten auf.
Um diesen Vergleich weiter zu spinnen:
Eine UART-ISR muss nicht unbedingt darauf reduziert sein, nur noch ein
Flag zu setzen, damit das Hauptprogramm dann das empfangene Byte in eine
Queue steckt. Das kann die ISR dann schon auch noch selber machen.
Rasputin schrieb:> Was hat sich für euch in der Praxis bewährt?
Am Ende vom Tag gibt es zwei Möglichkeiten:
a.) Die GUI benutzt die Applikation, oder
b.) die Applikation benutzt die GUI.
Such Dir was aus. Einer der beiden Komponenten ist Chef, einer ist
Laufbursche.
Für (ganz) große Sachen verkompliziert man das gerne:
c.) Die GUI und ein Vermittler haben ein Verhältnis und die Applikation
hat mit dem selben Vermittler ein Verhältnis.
Die Idee hinter 'c' ist, dass GUI und Applikation nichts voneinander
wissen und somit beide ihre eigenen Wege gehen können. Dafür hat man 50%
mehr Arbeit. Ob's was nützt? Wer weiß das schon...
Einen Tod musst Du sterben. Such Dir einen aus.
Hallo,
zer0 schrieb:> Jede in einer Klasse definierte Funktion sowie jede> Funktion, die so eine "Objekt"-Struktur übernimmt, deklariert damit> implizit, dass ihr Ergebnis von sämtlichen Eigenschaften des Objekt> abhinge.
du wirkst ein wenig überinvolviert. Nach welcher Logik sollte so etwas
implizit folgen? Wer würde so etwas ernsthaft fordern?
Eine der am häufigsten verwendeten member-functions dürften getter sein,
wer würde ernsthaft annehmen, ein getter müsse von allen members
abhängen? Das wäre schon sehr sehr exotisch.
> Schaut man sich jedoch konkrete Implementierungen an, wird man> in 99.9% der Fälle feststellen, dass man da jede Menge unbenutzte> Eingaben zurück behält.
99.9% der Fälle? Dann sollte es Dir ja ein leichtes sein, einfach mal
drei vier Fälle aus einem verbreiteten originär in C++ entwickelten
Framework zu zitieren, in denen es sinnvoll (und möglich) wäre aus einer
private member function eine public static zu frickeln. Da wäre ich
schon etwas neugierig.
Ich programmiere jetzt seit fast 30 Jahren und habe so einen Fall noch
kein einziges Mal in originärem C++ Code gesehen, aber na gut, ich habe
auch noch nie eine Blindschleiche gesehen, die gibts aber auch.
Also dann: Butter bei die Fische. Nicht Tutorial Code, sondern
verbreiteten produktiven originären C++ Code für member functions die
wegen mangelnder Abhängigkeit von den members in public static
gefrickelt werden sollten.
vlg
Timm
P.S: Kannst Du überhaupt C++? Ein wenig erscheint es mir, dass Du C++
einfach für einen makrolastigen aufgebohrten Assembler hältst :-))
René H. schrieb:> Aha, das hat mit dem Thema genau was zu tun? Wenn du mit OO oder> C++ ein> Problem hast, mach bitte einen eigenen Thread. Das gehört nicht zum> Thema vom TO.>> Grüsse,> René
Dann hast du die Sache einfach nicht verstanden: Was auch immer man so
als "gute Technik" vorschlagen könnte endet, konsequent beibehalten,
absehbar in einer einzigen Katastrophe! Das ist der Punkt!
Für das Scheitern des OOP-Ansatzes habe ich nur zu viele Beispiele
parat...
Timm R. schrieb:> du wirkst ein wenig überinvolviert. Nach welcher Logik sollte so etwas> implizit folgen? Wer würde so etwas ernsthaft fordern?>> Eine der am häufigsten verwendeten member-functions dürften getter sein,> wer würde ernsthaft annehmen, ein getter müsse von allen members> abhängen? Das wäre schon sehr sehr exotisch.
Och, das wird u.U. ein ernsthaftes Problem, wenn z.B., wie in java,
typischerweise genau ein Mutex den Zugriff auf das Objekt regelt. Fängt
man da erst einmal notgedrungen an, Untermengen von Members mit
verschiedenen Mutexes auszustatten wird es erstens unübersichtlich und
zweitens hat sich die Klasse als solche dann eigentlich schon überlebt.
Nur, was das eigentlich für ein Objekt sein sollte, dass aus eben so
einer beliebigen Untermenge an Attributen besteht, wird wohl kaum einer
sagen können. Da hat man dann die Situation, dass z.B. wie in einem
Formular sich einige Eingabefelder gegenseitig beeinflussen und andere
nicht. An manchen kann man gleichzeitig rumschrauben, bei anderen lässt
man das besser. Deklariert ist da aber A::b(). Da muss man also wohl A
(also alle members) locken, oder nicht?
Timm R. schrieb:> 99.9% der Fälle? Dann sollte es Dir ja ein leichtes sein, einfach mal> drei vier Fälle aus einem verbreiteten originär in C++ entwickelten> Framework zu zitieren, in denen es sinnvoll (und möglich) wäre aus einer> private member function eine public static zu frickeln. Da wäre ich> schon etwas neugierig.
Bitte - kein Problem. Nehmen wir einmal http://www.crystalspace3d.org/.
Schau mal in CSArchive: zwei wunderbare private Funktionen zum
Formatieren von Zeitstempeln! Ist doch ausgeschlossen, dass irgendwer
sowas nochmal brauchen könnte, oder? Wenn ich den Source lesen wollte
könnte ich auch noch Fragen stellen wie: Wie parse ich einen
Targa-Header im Datenfile? Wo ist das Mapping vom CS-TextureType hin zum
GLenum? usw. Irgendwo stünde da bestimmt gut versteckt und gewrappt eine
private-Function.
Laut einer Studie zum Thema Fehleranfälligkeit von Programmiersprachen
sind übrigens funktionale Sprachen am besten zu beherrschen.
http://rayb.info/uploads/fse2014-lang_study.pdf
zer0 schrieb:> Wobei die Softwarequalität in der Entwicklung LÄNGST nicht in dem Maße> zunimmt wie die Leistungsfähigkeit der Hardware. Was sagt uns das?
Das sagt uns, daß Softwarequalität nichts mit der Leistungsfähigkeit der
Hardware zu tun hat. Meine Güte.
Sheeva P. schrieb:> zer0 schrieb:> Wobei die Softwarequalität in der Entwicklung LÄNGST nicht in dem Maße> zunimmt wie die Leistungsfähigkeit der Hardware. Was sagt uns das?>> Das sagt uns, daß Softwarequalität nichts mit der Leistungsfähigkeit der> Hardware zu tun hat. Meine Güte.
Also wenn Du auf heutiger Hardware immer noch das selbe fabrizierst wie
damals auf dem C64 - in Sachen UX z.B. - wäre das schon fast wieder
sympathisch:)
Rolf M. schrieb:> Nun gut, wenn man auf alles blockierend wartet, kommt man ohne Threads> natürlich nicht weit. Aber nur um auf Daten zu warten, braucht man> meisten nicht gleich einen Thread.
Sachen wie fread/fwrite ruft man einfach so auf. Nur daß das mit
Netzlaufwerken und VPN auf einmal durchaus merkliche Latenzen haben
kann, und das will man nicht in einer GUI.
Die einzige Methode, wie man garantiert, daß die GUI nicht lahmt, ist
die, daß der GUI-Thread so gut wie nichts tut.
> Eine UART-ISR muss nicht unbedingt darauf reduziert sein, nur noch ein> Flag zu setzen, damit das Hauptprogramm dann das empfangene Byte in eine> Queue steckt. Das kann die ISR dann schon auch noch selber machen.
Um im Vergleich zu bleiben: Sie sollte die Daten aber auch nicht in ein
SPI-Flash wegspeichern.
zer0 schrieb:> Laut einer Studie zum Thema Fehleranfälligkeit von Programmiersprachen> sind übrigens funktionale Sprachen am besten zu beherrschen.> http://rayb.info/uploads/fse2014-lang_study.pdf
Jetzt müßte man natürlich die Frage stellen, woran das liegt. An der
Sprache oder ihrem Paradigma, wie Du andeuten willst? Oder, zum
Beispiel, vielleicht daran, daß einige Sprachen mehr Anfänger anziehen
als andere? Oder daran, daß einige Sprachen tendenziell eher für
einfache, andere hingegen für komplexe Probleme ausgewählt werden? Oder
daran, daß einige Sprachen einen besseren Library-Support bieten als
andere?
Man weiß es nicht. Deine Studie sagt dazu überhaupt nichts und läßt
daher auch keine belastbaren Aussagen über die Sprachen oder ihre
Paradigmen zu. Dann könnte man aus der Farbverteilung von KFZ darauf
schließen, daß rote Autos besser seien als gelbe.
Immerhin gibst Du Dir Mühe, Deinen Unsinn eloquent zu verpacken. Aber
daß der Bullshit hübsch verpackt ist, ändert ja nichts daran, daß es
sich immer noch um ebensolchen handelt. Zudem erwecken Deine Wortwahl
und Deine "Argumente" den Eindruck, daß das Thema für Dich äußerst
emotional besetzt ist, und daß Du keinen Wert auf eine sachliche,
faktenbasierte Auseinandersetzung legst. Naja, Du weißt selbst am
Besten, wie Du wahrgenommen werden willst.
Ja, richtig. Dadurch, dass Unfallstatistiken von bestimmten Modellen
dominiert werden, ist noch gar nichts gesagt! Sowas muss man vor allem
als Offizieller beherrschen...
Was man aber jedenfalls aus der Studie folgern kann, ist, das was auch
immer so an empfohlenen Techniken Standard ist, diese immer noch
offensichtlich schlecht skalieren auf große Projekte.
zer0 schrieb:> diese immer noch> offensichtlich schlecht skalieren auf große Projekte.
bei wieviel LOC fangen denn große Projekte an?
Ich schmeiße hier mal 500k LOC die ich vor mir habe in die Waagschale.
Abradolf L. schrieb:> zer0 schrieb:> diese immer noch> offensichtlich schlecht skalieren auf große Projekte.>> bei wieviel LOC fangen denn große Projekte an?>> Ich schmeiße hier mal 500k LOC die ich vor mir habe in die Waagschale.
Und was sagt die Statistik über das Verhältnis der Anzahl an
fehlerhaften Commits zu Commits überhaupt? Steigt's auch
überproportional?
zer0 schrieb:> Abradolf L. schrieb:>> zer0 schrieb:>> diese immer noch>> offensichtlich schlecht skalieren auf große Projekte.>>>> bei wieviel LOC fangen denn große Projekte an?>>>> Ich schmeiße hier mal 500k LOC die ich vor mir habe in die Waagschale.>> Und was sagt die Statistik über das Verhältnis der Anzahl an> fehlerhaften Commits zu Commits überhaupt? Steigt's auch> überproportional?
Was soll ein fehlerhafter Commit sein?
Was macht denn dein Programm überhaupt? Rechnet es lange, sind es
mehrere Tasks oder lässt sich jede Aktion schnell innerhalb eines
Listeners zum jeweiligen GUI-Element abarbeiten?
Bei ersterem musst du viel mit Threads arbeiten, diese sauber verwalten,
evt. ne eigene Verwaltungsschicht mit reinbauen wenn nicht schon durch
ei Framework vorgegeben bei letzterem klatschst du halt jeden Listener
mit entspr. Code voll.
Da gibt kein richtig oder falsch es kommt drauf an was du vor hast.
Abradolf L. schrieb:> zer0 schrieb:> Abradolf L. schrieb:> zer0 schrieb:> diese immer noch> offensichtlich schlecht skalieren auf große Projekte.>> bei wieviel LOC fangen denn große Projekte an?>> Ich schmeiße hier mal 500k LOC die ich vor mir habe in die Waagschale.>> Und was sagt die Statistik über das Verhältnis der Anzahl an> fehlerhaften Commits zu Commits überhaupt? Steigt's auch> überproportional?>> Was soll ein fehlerhafter Commit sein?
Einer, der aufgrund eines Fehlers darin noch einmal nachgepatcht werden
muss und/oder im Codereview hängen blieb? Keine Ahnung. Frag die
Maintainer.
Der Punkt ist, dass das eigentlich völlig ausgeschlossen sein sollte,
wenn die App nur aus für-sich abgeschlossenen Modulen bestünde. Aber
leider schafft zB ein Listener das Problem des konkreten Behaviours ja
nicht aus der Welt sondern verlagert es nur auf später - an eine Stelle,
wo vom konkreten Kontext seines Aufrufs nichts mehr zu sehen ist. Dann
kaskadieren die Dinger und alles geht gut - bis einmal einer den anderen
im Stack der Call-Kette löscht und sich die Katze so in den Schwanz
beißt. Äußerst schwer zu finden. Hätte man sauberen, linearen Code vor
sich, wäre das Problem evident oder gar nicht aufgetreten.
zer0 schrieb:> bis einmal einer den anderen> im Stack der Call-Kette löscht und sich die Katze so in den Schwanz> beißt.
Der kommt mit so einem Murks dann nicht auf den Main-Branch weil der
Gated-Check-In das verhindert wenn irgendein Test umfällt.
Komponentenübergreifende Schnittstellenänderungen finden nur mit
Absprache der Architekten statt und dann wird erstmal geguckt wo dadurch
überall Arbeit entsteht uswusf.
Ansonsten können die einzelnen Komponenten ziemlich isoliert voneinander
entwickelt werden.