Große saubere Architekturen (mit UI?)



  • Hi,

    ich merke, dass ich als "Klein-Architekt" so langsam an meine Grenzen stoße oder noch viel lernen kann. Zwar ist das alles ganz gut erweiter- und änderbar, aber ich will mehr. Ich merke immer wieder Designfehler, aber erst später und nicht bereits, wenn ich sie mache. Ich habe das Gefühl, da fehlt mir einfach noch die Erfahrung, ob ich ein Problem generell auf eine bestimmte Art und Weise oder eben eine andere löse. Folge ist aufwendiges Refactoring, was Zeit kostet.

    Dabei ist ein Problemfeld z.B. UI-Programmierung und ein anderes sauberes Einbetten von Threads in eine Software. Da habe ich ein paar Standardmechanismen drin, die jeder kennt, wie Trennung von Backend und Frontend oder für Threads z.B. ein Threadpool.

    Kennt jemand ein Opensource-Projekt, was auch UIs nutzt, und sauber strukturiert ist, gleichzeitig auch Threads nutzt? Und am Besten auch Undo/Redo-Funktionalität und all die Späße, die ein großes GUI-Projekt hat. Das natürlich in C++. Ich glaube, indem ich mir so was Mal anschaue, könnte ich eine ganze Menge für mich lernen. 🙂

    Wäre super, wenn ihr da ein paar Beispiele hättet!



  • Open/Libre Office? Ich hab mir den Code noch nie angeschaut, aber ich schätze, wenn einer Projekt dieser Größe komplett unsauber aufgebaut wäre, hätten die Entwickler es schon lang an die Wand gefahren. Dann hab ich gehört, dass Google Chrome recht sauber strukturiert ist.
    Versteh dein Problem aber ehrlich gesagt nicht wirklich. Aus meiner Sicht ist die UI Architektur ziemlich flach und die einzelnen Komponenten da sind voneinander völlig unabhängig. Wenn du die Daten von der Darstellung trennst, würd ich mir wegen "UI Architektur" keine großartigen Überlegungen machen. Ist eh meist der langweilige Routineteil.
    Threads sind generell ein recht komplexes Thema. Kann auch immer wieder passieren, dass man einiges umbauen muss, wenn man auf Multithreading umstellt. Ich glaub aber nicht, dass es dir da großartig weiterhilft, in andere Projekte reinzuschauen.



  • Denke, auch den Firefox-Code könntest du dir mal anschauen:
    http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/



  • Alles nur nicht den Firefox-Code! 😃



  • Groß und sauber gibt es nicht! Das funktioniert nur im Kleinen.



  • cooky451 schrieb:

    Alles nur nicht den Firefox-Code! 😃

    Der sauberste ist er vielleicht wirklich nicht, jedoch bekommt man mal ne Vorstellung vom Aufbau.



  • Ich weiß nicht, ob Firefox hier überhaupt ein geeignetes Beispiel ist. Ok, ich hab auch Chrome vorgeschlagen, passt hier wohl auch nicht so ganz (ist aber denke ich sauberere als Firefox). Browser haben wohl eine andere Vorstellung von UI als der TE.



  • Grundsätzlich würde ich alles nehmen. Aber UI voneinander unabhängig und flach?

    Ich habe in meinem Projekt ein Fenster, darin enthalten ist ein Tree, von dem jedes Tree-Element natürlich auch eine Klasse ist, dann habe ich dort ein Dropdown, dessen Editfeld eine eigene Klasse ist, die Anzeige vom Dropdown-Arrow nebst den darin enthaltenen Elementen ebenfalls (muss man so machen, wenn man eine abweichende Darstellung haben möchte) und diverse Elemente vom UI triggern Neuberechnungen, die parallel abgehandelt werden, also brauche ich dort eine Signal/Slot-Verbindung zum Backend.

    Das Backend sollte aber nicht den gleichen Signal/Slot-Mechanismus verwenden wie das UI (UI wäre z.B. QT, während ich Signal/Slots aber nicht im Backend so abbilden möchte, weil QT eben nur als UI dienen soll). Also muss ich das entkoppeln.

    Und dann möchte ich eine Undo/Redo-Funktionalität einbauen. Das macht man üblicherweise mit dem Visitor-Pattern (oder geht das in C++ schöner?), also gehört das auch dazu.

    Aber Moment, die History-Actions sind nicht einfach nur an das Fenster gebunden, die History ist global, also braucht mein Fenster einen Verweis auf die "globale" History, nagut. Aber eigentlich ändert ja die History nur das Backend und das Frontend muss sich passend updaten. Also muss ich auf History-Recall das Backend ansteuern und gleichzeitig das Frontend ansteuern, das alles noch sauber entkoppelt, hmm...

    Ganz ehrlich, ich finde die UI-Programmierung wesentlich komplizierter als die Backend-Programmierung. Das Backend kriege ich meist sehr schnell sauber hin, aber das Frontend explodiert bei mir oft in Klassen. Flach ist da nicht wirklich viel.

    Ist halt immer die Frage, was man so hat. Wenn man eine reine Business-Anwendung hat, dann ist es oft leicht, weil die Darstellung auf dem UI sich in Grenzen hält.

    Aber was ist z.B. mit Photoshop? Da muss man irgendwie gescheit den Status des aktuellen Maus-Cursors beinhalten (ja, exakt dazu gibt es auch ein Pattern, ich weiß), davon abhängig gibt es zig Fenster, deren Inhalt sich dynamisch aufbauen muss. Diese Fenster sind aber auch gleichzeitig davon abhängig, in welchem Zeichenfenster man sich befindet. Und dort hängt das natürlich auch noch vom Farbmodus des Bildes, von der aktuellen Ebene, von der aktuellen Farbmaske uvm. ab.

    Undo/Redo ist da natürlich völlig klar und man kann jede einzelne Aktion auch speichern und eine Stapelverarbeitung drauf ausführen.

    ---

    Also ich finde, UI-Programmierung ist jetzt nicht so flach und trivial. Die von euch genannten Beispiele klingen schon Mal interessant (ja, Browser sind da wohl etwas anders gestrickt, da stelle ich mir die Architektur gerade etwas simpler vor, auch wenn die natürlich auch massiv Multithreading nutzen).

    So ganz überzeugt scheint ihr ja nicht von größeren, sauberen Architekturen überzeugt zu sein, hat hier wirklich keiner ein Beispiel? SFML meinte ja Mal jemand, das klingt per se ja gar nicht so uninteressant, wobei ich nicht weiß, wie groß das Projekt ist.

    Ich schau da jedenfalls Mal rein, aber vielleicht hat jemand noch einen Vorschlag, von dem er wirklich überzeugt ist und dessen Code er kennt. Ich meine, "learning by doing" dürfte doch wirklich etwas lange dauern, da muss man ja 100 große Projekte machen, bevor man wirklich gut ist, da ist man doch vorher tot. Hier sind doch echt viele richtig gute Coder, habt ihr euch nie an irgendwelchen Projekten orientiert?



  • learning by doing

    Es gibt keine Abkuerzung.

    gibt es auch ein Pattern

    Ja, beispielsweise Model-View-Controller. Besinne dich aber nicht zu sehr auf Patterns, sondern gebrauche deinen eigenen Kopf.

    Was ist das Problem bei Redo/Undo. Du hast ein Befehl+Parameter, beispielsweise Auschneiden oder Farbanpassungen. Beim Ausfuehrungen musst dir die inverse Operation dazu merken.



  • Na ja, aus fremden Architekturen lernen ist für mich eine Hilfe, die man sich nicht einfach aus der Nase ziehen kann. Schaut man sich nie so was an, lernt man doch bestimmt weniger schnell als wenn man immer nur sein eigenes Brot backt. Je nach Definition finde ich also schon, dass es "Abkürzungen" gibt.

    Beim Ausfuehrungen musst dir die inverse Operation dazu merken.

    Es gibt da kein wirkliches Problem, aber man schafft sich überall, wo Aktionen durchgeführt werden können, zusätzliche Abhängigkeiten zur History-Klasse (oder wie man die nennen mag). Und das führte ich als ein Beispiel der vielen Beispiele an, um zu zeigen, dass UI-Code eben doch ziemlich komplex und abhängigkeitsbeladen sein kann.

    Ich habe mir eben Mal Chromium angeschaut und ein paar Facetten der Architektur auch verstanden. Erscheint mir eigentlich schon ganz sauber, zumindest abstrahieren die viel mehr als ich (überall Delegates usw.) und haben eine recht klare Ordnerstruktur. Was wäre da denn eure Kritik?

    Und in einem Stackoverflow-Thread wurde das hier empfohlen: http://www.amazon.de/Large-Scale-Software-Addison-Wesley-Professional-Computing/dp/0201633620 (wohl erwähnend, dass das Buch alt ist und die neuen C++-Mittel noch nicht einsetzt, aber ich kann ja auch beim Lesen kritisch reflektieren). Kennt das jemand, haltet ihr was davon?



  • Dass jeder Knoten im dem Tree eine eigene Klasse ist, ist für mich eigentlich Anwendungslogik und keine GUI. Und für die GUI schreibst du ein entsprechendes QAbstractItemModel (du machst ja Qt).
    Eigene Klassen für Editfelder, Arrows (was auch immer du meinst) usw. sind auch ok. Sind halt Klassen. Da ist ja noch keine Architektur dabei. Wenn du eine entsprechende Klasse brauchst, schreibst du die eben. Und das ganze sollte auch möglichst flach sein. D.h., wenn du eine Editklasse schreibst, sollte sie möglichst unabhängig und wiederverwendbar sein und nicht nur mit einem bestimmten Model arbeiten. Wenn du aber doch mal ein Widget brauchst, das nur mit bestimmten Daten arbeiten kann, weils halt nicht anders geht oder einfach nach dem YAGNI Prinzip, ist es auch völlig in Ordnung.
    Was das Backend macht hat nichts mit UI Architektur zu tun, die du ja vor allem angesprochen hast. Da kannst du machen was du willst, da sind Architekturfragen aus meiner Sicht auch viel wichtiger. Und den Kommunikationsmechanismus kannst du ja durchaus entkoppeln. Du könntest im Backend z.B. Observer verwenden, und in der UI dann Signals und Slots, seh da kein Problem. Oder du verwendest auch im Backend Signals und Slots, könnte durchaus auch eine Option sein, muss aber nicht.
    Für Undo/Redo nimmt man das Memento Pattern, seh ich hier erstmal keinen Zusammenhang mit Visitor. In Qt gibts schon eine entsprechend Implementierung, z.B. mit den Klasse QUndoCommand und QUndoStack. Das in der Gui zu implementieren ist kein Problem. Ist die Frage, ob du das auch im Backend brauchst. Ist von Fall zu Fall zu unterscheiden. Aber wenn, dann hat es wieder wenig mit dem UI zu tun. Würde auch im Backend einfach eine ähnliche Mementostruktur aufbauen, hab ich schon öfter gemacht. Meist braucht man Undo/Redo aber tatsächlich nicht unbedingt im Backend und es reicht völlig, das im Frontend zu implementieren.

    Ob Photoshop hundert Fenster oder 20 000 hat, ist eigentlich relativ egal. Die sind unabhängig voneinander. Es gibt sicherlich irgendeinen zentralen Kommunikationsmechanismus, z.B. Pub/Sub oder Signals/Slots. Aber jedes Fenster ist voneinander völlig unabhängig. Im Backend hast du hingegen sehr viele Abhängigkeiten, die exponentiell steigen können. Mich würde schon durchaus interessieren, wie Photoshop aufgebaut ist, aber die GUI juckt mich dabei überhaupt nicht.



  • History-Klasse (oder wie man die nennen mag). Und das führte ich als ein Beispiel der vielen Beispiele an, um zu zeigen, dass UI-Code eben doch ziemlich komplex und abhängigkeitsbeladen sein kann.

    History ist kein GUI-Element!



  • Okay, da sieht man doch sehr viele meiner Defizite. Und vielen Dank für die ausführliche Erklärung, das hat mir schon Mal einiges geholfen. 🙂

    Ich glaube mittlerweile, ich habe nicht Mal die Grundprinzipien der Trennung und der Kommunikation kapiert. Also im Backend läuft im Prinzip wirklich alles ab und auf dem UI wird stets nur die Repräsentation des aktuellen Zustands des Backends abgebildet. Aber wo "legt man das Backend denn überhaupt ab"? Die Eventloop läuft ja über das UI (besser gesagt, über QT, aber für mich ist QT einfach nur die UI-Bibliothek; bei der Betrachtensweise hat man es aber irgendwie nie sauber entkoppelt), da könnte man ja denken, dass ein UI-Element zur Repräsentation eines Textes eine Komposition auf den Text hat. Macht man das so oder wie? Man muss ja an irgendeiner Stelle all die Klasseninstanzen des Backends ablegen. Wenn man nicht alles zum Singleton macht, wo legt man das denn dann ab?

    Und für das Updaten des Frontends basierend auf dem UI (oder auch andersrum) braucht man dann ja auch überall noch den Controller (wenn man das jetzt über MVC löst, wovon ich ausgehe). Aber dann hat man ja noch eine dritte Schicht, bei der ich mich frage, wo man die ablegt. Wo instanziiert man das alles? Da bräuchte ich wirklich Mal ein konkretes Beispiel, glaube ich...

    Vielleicht auch ein kleines, sauberes UI-Projekt in C++ mit QT. Aber eines, bei dem das Backend auch wirklich unabhängig von QT ist (bei der Suche nach QT-MVC-Beispielen fand ich dann nämlich auf Backend-Seite ein QTreeWidgetItem...)



  • Na ja, aus fremden Architekturen lernen ist für mich eine Hilfe, die man sich nicht einfach aus der Nase ziehen kann.

    Naja, so manche Architektur ist aber auch nach dem "Friss oder Stirb" Design Pattern gestrickt. 😉

    Meines Erachtens kannst du schon einiges lernen, wenn du Design Pattern anschaust, welche für meinen Geschmack eher einen Ideenpoll darstellen. Denn auch Design Patterns sind keine eierlegende Wollmilchsäue. Eigene Ideen sind da wichtig. Aber man sollte schon einige Pattern verstanden haben. Und eine gute Portion Objektorientierung ist schon fast Pflicht.



  • Na ja, die Patterns kenne ich alle, auch wenn ich sie nicht immer alle benutze, weil ich da in der Praxis nicht immer Sinn sehe. Die wesentliche Frage ist auch immer, wann man eine Abstraktion einbaut und wann nicht. Over-Engineering ist ja auch ein Anti-Pattern.

    Den Schritt habe ich also schon gemacht, genützt hat mir das bisher nur sehr wenig.



  • Eisflamme schrieb:

    Aber wo "legt man das Backend denn überhaupt ab"? Die Eventloop läuft ja über das UI (besser gesagt, über QT, aber für mich ist QT einfach nur die UI-Bibliothek; bei der Betrachtensweise hat man es aber irgendwie nie sauber entkoppelt), da könnte man ja denken, dass ein UI-Element zur Repräsentation eines Textes eine Komposition auf den Text hat. Macht man das so oder wie?

    🙄

    Was hat das Backend mit der Eventloop zu tun? "Komposition auf den Text" Und UI = Frontend.

    Aber ich hab zumindest verstanden, was du mit "Backend ablegen" meinst 😉 Also, zum einen darfst du dir "das Backend" nicht unbedingt als ein Objekt vorstellen. Es muss da überhaupt kein zentrales Objekt geben. Es ist das Modell im MVC Pattern, also die eigentliche Logik. Und es kann auch aus hunderten separaten Klassen bestehen. Also kein Singleton Backend Objekt.
    Einzelne UI Elemente brauchen immer nur einzelne Backend Elemente, die sie gerade kennen. Und die Objekte kann z.B. das Hauptfenster verwalten. Stell dir vor, du schreibst einen CSS-Editor. Im Backend hast du lauter Klassen, die die eigentliche Logik implementieren. Das ganze ist völlig unabhängig von einer GUI und kann in verschiedenen Szenarios wiederverwendet werden, z.B. intern in irgendeinem Programm, um on the fly CSS zu generieren und zu modifizieren. Und jetzt schreibst du ein entsprechendes grafisches Programm, um CSS Dateien zu bearbeiten. Wo brauchst du da ein Singleton? Sagen wir, du hast ein MainWindow. Das kann ja schon eine Instanz oder auch zig Instanzen deiner Backendklassen verwalten, z.B. CssDocument. Oder du machst MDI und dann verwaltet jedes MDI Fenster eine Instanz. Dann schreibst du z.B. eine QAbstractItemModel Ableitung, die Daten aus deiner Klasse entsprechend verpackt. Und dann hast du lauter verschiedene Fenster, eins von denen zeigt z.B. die Baumstruktur an. Das bekommt dann einen Zeiger auf ein CssDocument, und erstellt ein CssDocumentModel und gibt dem das Dokument rein. Und dein MainWindow erstellt das entsprechende Fenster und gibt ihm die Dokumentinstanz rein, die es verwaltet. Und wenn du im Baumfenster dann einen Rechtsklick machst und ein anderes Fenster aufrufst, z.B. Farbe bearbeiten, dann braucht das Farbe bearbeiten Fenster vielleicht einen Zeiger auf ein CssElement. Das ist aber eine Aktion in dem Baumfenster. Das holt sich auf dem Model das selektierte Element, instanziiert ein Farbeditorfenster und gibt das CssElement rein. Usw. Du brauchst hier nirgends Singletons.
    Der Controller ist für mich eigentlich das QAbstratItemModel. Das ist jetzt alles nicht so scharf definiert, aber das spielt für mich am ehesten die Rolle des Controllers. Oder wenn du keins hast, dann ist der Code in dem entsprechenden Fenster der Controller. Man braucht kein Klassen, die "Controller" heißen, hab ich noch nie gemacht.
    Updaten des Frontends ist jetzt eigentlich auch kein Problem. Wenn dein Farbeditor mit Ok beendet wird, schreibst es die Werte aus den Widgets in die enstprechende CssElement Instanz, die es im Konstruktor bekommen hat. Und da dein Model auf dieselbe Instanz zugreift, zeigt es auch automatisch die richtigen Werte an.



  • Schreib doch seperat zum QT-UI noch ein Console-UI dann ist fast sichergestellt das alles getrennt ist. :p



  • @Eisflamme:
    Hmmm, ich kenne deinen Wissensstand nicht. Aber es hört sich ein wenig nach strikter Programmierung an.

    Vielleicht wäre es auch gut ein kleines Projekt (Notepad++, Notepad++ Plugins) zu nehmen und daraus mal ein Klassendiagramm, Sequenzdiagramm ableitest. Nimm dazu am besten ein Programm wie Argouml. Ziel des Ganzen: Erst denken, dann Struktur festlegen dann programmieren.

    Sagt dir der Begriff Divide & Conquer was?



  • Hi :),

    Mechanics:
    Okay, super Beispiel, genau so was wollte ich. Man kann also im MainWindow einfach "das Backend" initialisieren. Klar, das Backend besteht aus vielen unterschiedlichen Klassen, aber wenn die eben alle miteinander in Verbindung stehen, dann hat man ja meistens nen Single Point of Access irgendeiner Form. Wenn man den nicht als Singleton ablegt (was ich auch nicht gut finde, das war nur eine Lösungsmöglichkeit), muss man eben über Dependency Injection in jede Fensterklasse die Backend-Klassen-Referenzen durchreichen, die dort benötigt werden.

    QAbstratItemModel als Controller verstehe ich, ergibt auch wesentlich mehr Sinn. Ich dachte nämlich wegen des Namens immer, das solle man wirklich als Model nutzen... und das ergibt für mich gar keinen Sinn, weil es immer noch QT-bezogen ist. Damit ist also auch die Frage beantwortet. 🙂

    Ich kenne jetzt von einigen Leuten Architekturen, die bewusst zwischen Model und View Controller-Klassen einfügen, also eben ganz konsequent immer. Da wäre das QT-Pendant dazu ja einfach immer die View-Klassen statt Widget-Klassen zu nehmen, wo man also immer die "Controller-Klassen" (die aber auf Model enden) dazwischen hat. Du würdest aber nicht einen Bereich "controller" nennen? Chromium hat ja einen Bereich "ui" und einen Bereich "content" (ich schätze Mal, content steht dann für das Model).

    tui:
    Das wäre der Check, ja.

    Bitte ein Bit:
    Hintergrund ist wie gesagt, dass ich die Patterns zwar alle theoretisch kenne, aber alles, was mit Front/Backend-Strukturierung zu tun hat, nie eingesetzt habe, weil ich relativ neu in der UI-Programmierung bin und vorher nur Konsole oder etwas Grafikprogrammierung gemacht habe.

    Vielleicht wäre es auch gut ein kleines Projekt (Notepad++, Notepad++ Plugins) zu nehmen und daraus mal ein Klassendiagramm, Sequenzdiagramm ableitest.

    Klingt gut. Wegen sowas habe ich ja im OP auch gefragt, ob jemand saubere Architekturen kennt, damit ich die analysieren kann. ArgoUML kenn ich nicht, schau ich mir Mal an. 🙂

    Divide & Conquer kenne ich vom Quicksort, sonst aber nicht.

    Danke für die ganzen Anregungen bisher, das hilft mir alles schon deutlich weiter. 🙂



  • Ich glaube du solltest net so viele Buecher ueber Designpatterns lesen und einfach mal machen.

    als Singleton ablegt

    Nun Model bzw. Controler werden einfach als normales Objekt global definiert und bei der Konstruktion ger GUI-Elemente einfach als Parameter mitgegeben.

    Dependency Injection

    Habe nie verstanden, was der Clou daran ist.

    aber alles, was mit Front/Backend-Strukturierung zu tun hat, nie eingesetzt habe

    Da gibt es bestimmt noch mehr. Probiere doch mal, das Backend als Service zu implementieren, wobei Frontend und Backend nur mit Nachrichten kommunizieren.

    Divide & Conquer

    Das kann man auf fast alles anwenden.


Anmelden zum Antworten