Design-Frage zur Vermeidung von Getter und Setter



  • Hallo,
    ich habe mehrere Klassen, von denen jeweils einige Eigenschaften/Attribute gespeichert werden sollen, damit sie nach einem Neustart des Programms wieder geladen werden können und zur Verfügung stehen.

    Jetzt jede Klasse ihre eigene Datei zu schreiben, wäre wohl etwas übertrieben. Also dachte ich mir, ich nehme zum speichern und laden eine eigene Klasse (configdaten), die eine Datei verwaltet. Nun fing ich an die einzelnen Werte mittels Getter und Setter an diese Klasse zu übergeben, bis ich merkte dass dies auch nicht so optimal ist. Die Kapselung ist weg und die Liste wurde ziemlich lang.

    Momentan wäre meine Idee die Werte in den Klassen selber in einen stream zu schreiben und den stream an die Klasse (configdaten) mittels write, read oder Operator << >> zu übergeben. Allerdings müsste ich einen Identifikator mit übergeben, damit die Klasse weiß, wem welche Daten gehören.

    Gibt es vielleicht einen noch besseren Weg? Wie würdet ihr so etwas lösen?



  • Schau mal da: www.boost.org/libs/serialization/
    Du musst nicht zwingend diese Bibliothek verwenden, aber zumindest wird dir das grundlegende Konzept gezeigt, wie eine gute (ich sag jetzt absichtlich nicht beste) Implementierung aussehen könnte.



  • Hallo stefkowa,

    poste doch bitte mal einen Auszug der Datei, und markiere (bzw. nenne) die Werte, die von einer Klasse A und einer Klasse B benötigt werden.

    stefkowa schrieb:

    Momentan wäre meine Idee die Werte in den Klassen selber in einen stream zu schreiben und den stream an die Klasse (configdaten) mittels write, read oder Operator << >> zu übergeben. Allerdings müsste ich einen Identifikator mit übergeben, damit die Klasse weiß, wem welche Daten gehören.

    hört sich an, als ob die Werte, die den Objekten einer Klasse A zuzuordnen sind, wild über die Datei verstreut sind.

    Gruß
    Werner



  • Es geht um Settings, die entweder vom Entwickler oder dann vom Benutzer neu festgelegt werden können. Herkömmlich geht das nur über bereitgestellte Config-Dateien. Dieser Gesichtspunkt wurde bei C++ nicht oder unvollständig weiter entwickelt. Findet sich gut leider nur bei C# unter .NET (MS-abhängig) - Da haben einige Compilerbauer geschlafen oder andere Interessen verfolgt!



  • @särialisation: Ich habe es mal überflogen, aber ich glaube, es ist nicht das was ich suche.

    @Werner: Da gibt es zu Beispiel die Fensterklasse mit Position und Größe:

    class Window
    {
      ...
      privat:
        int x;       //
        int y;       // zu speichernde Werte
        int width;   //
        int heigt;   //
    }
    

    und die Editorklasse mit der Schrift:

    class Editor
    {
      ...
      privat:
        string font;   //
        int fontSize;  // zu speichernde Werte
    }
    

    @berniebutt: Ja, genau darum geht es.



  • Ich rate dir ganz stark davon ab, C++ und Windows-Programmierung gleichzeitig zu lernen. Das bringt mehr Nachteile als Vorteile, weil es dich in der Lernphase immer wieder auf die C-Schiene lenken wird. Das merkst du aber erst, wenn's zu spät ist. Arbeite lieber mindestens ein gutes C++-Buch (damit meine ich kein MFC) gründlich durch, dann kannst du immer noch auf Klickibunti umsteigen. Ist der Baum einmal groß, kannst du ihn schlecht biegen.



  • Weder lerne ich C++ und Windows Programmierung gleichzeitig, noch benutze ich überhaupt Windows. Ich habe auch einige C++ Bücher, darunter auch "Der C++ Programmierer" von Breymann und "Die C++ Programmiersprache" von Stroustrup, in denen ich auch immer wieder regelmäßig nachschlage. Leider konnte ich in den Bücher zu dem von mir genannten Problem keine wirkliche Lösung finden. Und damit der Baum nicht in die falsche Richtung wächst, bitte ich hier um Tipps und Ratschläge.



  • stefkowa schrieb:

    @Werner: Da gibt es zum Beispiel die Fensterklasse mit Position und Größe:

    class Window
    {
      ...
    

    Ich meinte jetzt die Konfigurationsdatei, und nicht den Sourcecode. Wenn Du von einer Windows-Klasse Position und Größe abspeicherst, so wäre es doch blöd, wenn die Werte in der Konfigurationsdatei nicht zusammen stehen würden.

    stefkowa schrieb:

    Momentan wäre meine Idee die Werte in den Klassen selber in einen stream zu schreiben und den stream an die Klasse (configdaten) mittels write, read oder Operator << >> zu übergeben. Allerdings müsste ich einen Identifikator mit übergeben, damit die Klasse weiß, wem welche Daten gehören.

    Die Streaming-Operatoren sind zunächst ein Weg gegen den nichts spricht. Es ist 'straight forward', Du brauchst keine fremden Libraries und es nicht sonderlich aufwendig.

    Was ich nicht nachvollziehen kann ist, warum Du meinst einen Identifikator mitgeben zu müssen, um die Zugehörigkeit der Daten zu regeln.

    Gruß
    Werner



  • Ich bitte einmal um Auskunft, wie man konkrete Eigenschaften (properties) eines Fensters oder Dialoges mit C++ vom Entwickler einstellen und vom Anwender jederzeit ändern und für den nächsten Programmlauf bereitstellen kann. Auch interessiert das Wiederaufsetzen auf vorherige Eingaben. Das war die Frage, so wie ich sie verstanden habe! Mir fällt da nur eine Config-Datei ein und die erfordert sehr viel eigenen Aufwand. Meine Compiler für C++ geben da keine besondere Unterstützung. An dieser Stelle gefällt mir C#, doch wozu muss gleich ein anderer Compiler her, mit dem man dann sofort Portierungsprobleme bekommt?



  • @Werner:
    Im Moment habe ich keine Config-Datei, da noch nach dem richtigen Weg suche.

    Im ersten Anlauf, mit den Gettern und Settern, habe ich die Werte einfach hintereinander binär in die Config-Datei geschrieben. Zusammengehörende Werte, wie die des Fensters, hatte ich direkt hintereinander.

    Angenommen config ist ein Objekt der Config-Klasse und stream der Stream mit den zu speichernden Werten. Wenn ich jetzt den stream an config übergebe

    // entweder mit
    config << stream;
    // oder
    config.write(stream);
    

    weiß config doch nicht welcher Klasse bzw. Objekt diese Werte gehören. Und beim abfragen/holen des streams von config, weiß config doch nur, dass ein stream angefordert wird aber nicht welcher bzw. für welches Objekt.



  • Also wenn man sich z.B. Mal Qt anschaut, gibt es dort eine Settings-Klasse.

    Dort wird es so geregelt, dass Du Gruppen hast, diese Gruppen enthalten wiederum eine beliebige Menge von benannten Attributen, deren Typ alles Mögliche sein kann (QVariant, bei Boost z.B. boost::any).

    Die Zuordnung von jedem einzelnen Objekt zu den jeweiligen Daten ist hartverdrahtet, d.h. man serialisiert nicht einfach ein Fenster, sondern man erstellt eben manuell die Gruppe "MainWindow" und gibt der die Attribute "xPosition, yPosition" - was auch immer.

    Es ist ja auch nicht sinnvoll von jedem einzelnen Element auf dem Fenster alle Eigenschaften zu speichern, da solltest Du stark selektieren.

    Meine Idee wäre daher gar kein generelles Konzept fürs Serialisieren zu entwickeln (im Sinne davon, dass es generisch für alle Klassen funktioniert), sondern einfach nur eine große Klasse zu haben, der man sagen kann "speicher bitte diese Zahl/diesen String unter dem Namen" und fürs Laden wieder zu interpretieren. Das wäre dann die Zugriffsschicht von Serializer <-> UI.

    Die Speicherung kann dann unterschiedlich erfolgen. Üblich ist unter Windows ja die Registry, Mac und Linux haben da wiederum andere Möglichkeiten - das ist also nicht portabel bzw. muss je nach BS neu entwickelt werden. Und wenn man es dann in einer Datei (z.B. ini oder xml) speichert, muss halt herausgefunden werden wie der jeweilige Benutzerordner auf dem Betriebssystem ist, in dem ein Nicht-Admin-Programm Schreib/Lesezugriff hat.

    Ich würde also erstmal schauen, ob das UI-Framework etwas Derartiges anbietet. Falls nicht, würde ich nach einer externen LIB schauen. Falls es da nichts Passendes gibt, würde ich es selbst schreiben, dann vermutlich über eine Datei und dort so etwas Ähnliches wie eine Gruppenlösung erzeugen.

    Ich weiß nicht, wie es C# handhabt, aber wenn man solche Eigenschaften wirklich von jedem Objekt speichern muss, setzt das Voraus, dass man auch jedes Objekt befüllen muss - und das heißt, dass man es nicht normal erzeugen kann, sondern die Erzeugung abstrahieren muss, etwa über eine Factory o.ä. Das bläht das Projekt konzeptionell also auf, obwohl man nur für einen Bruchteil der Objekte überhaupt etwas speichern muss.

    Möchte man ein Default-Schreib/Lese-Verhalten für eine bestimmte Klasse erzeugen, erscheint es mir ebenfalls gut operator<<, >>, read, write zu überschreiben, dann aber natürlich von einer Instanz aus zu verwenden, die eben weiß, was gespeichert werden muss. Die muss dann auch dafür sorgen, dass bei Programmstart alle Daten in die entsprechenden Objekte gefüllt werden.

    Hängt halt jetzt von den Details ab. So viel muss man imo meist gar nicht speichern und es bei Programmstart neu laden.


Log in to reply