Results 1 to 4 of 4

Thread: A few questions on good practices

  1. #1
    Join Date
    Nov 2019
    Location
    Lyon, France
    Posts
    18
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default A few questions on good practices

    Hi!

    I'm currently writing the save/load system of my application and I have a few questions about practicality and good practices.
    I have a lot of data that are used by different classes. So I create another class which hold all the data of the project (or at least, the data that are shared by other classes and the one I want to save).
    Is it a good way to do thing or is it wrong (the data can basically be accessed by any other class)? And if not, how should I design my save/load system to get/set all the information I want to save/load in a practical way?

    The other question I have is about slot/signal organisation. Each time something change on the screen, a lot of other elements have to adapt. By looking of what I did with the centralization of the variables, I thought I could do the same with the slots and signals. Like make another class which only serve as a hub to connect signals of some classes to the slots of other classes. But I think it may be going to far? But on the other hand, my connections start to become a little ovewhelming and it sounds like it could make things easier this way.

  2. #2
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: A few questions on good practices

    Is it a good way to do thing or is it wrong (the data can basically be accessed by any other class)? And if not, how should I design my save/load system to get/set all the information I want to save/load in a practical way?
    In most of my applications, I have the concept of a "document" class. This class holds everything that is relevant to the context of that "work unit" - parameters, results, document-specific user or display preferences, etc., as well as the contents of whatever the document is about. An app might have only a single "document" or it could have multiple. The document class usually holds other class instances as member variables that encapsulate things like inputs, parameters, results, etc.

    The document does not know how to save or load itself. For this, I have a separate set of classes I call "serializers" which know how to save the document and all of its contents to a file. If the document contains other class instances as member variables, each of those also has a serializer that knows how to save / load that class. So saving a document typically involves creating whatever the storage is, and then calling the serializer for the document, which calls the serializers for each of the items in the document. The first thing any serializer writes or reads is a version number. Each element in a document has its own unique version number, and the serializers use those version numbers to maintain backward compatibility.

    I often use XML to store documents. So I will have Qt DOM-based serializers. Each one takes a pointer to the thing being serialized as well as a pointer or reference to the DOM document. The serializer creates child elements as appropriate and adds them to the document. On load, the reverse happens - the serializers read the XML and adds the appropriate items to the document.

    Separating the document from how it is stored makes it very flexible. If I want to switch from XML to some other format, the document doesn't change, I just create a new set of serializers for that format (or I add new methods to the same serializers to support the new format).

    The other question I have is about slot/signal organisation.
    For this, I have a QFlags structure in the document class, where each individual flag represents a "hint" that some part of the document has changed. If multiple things change, these hints can be OR-ed together. When something changes, the document emits a "somethingChanged( Document * doc, QFlags hints )" signal. Other parts of the app connect to this signal. When they receive it, they examine the hints and if one applies to them, they update themselves by retrieving the updated information from the document.

    Using a single signal with a "hint" parameter avoids the problem of having a separate signal for each piece of information and makes it easy to add new hints as you add new features to the document. Likewise, you don't need a separate slot for each signal, you need just one, and inside the slot you look to see if the hints apply to you. There is a small amount of overhead with this because it is more like a broadcast than point-to-point connections so a lot of the signals are "wasted" on slots that don't care about them, but the reduction in the complexity of the signal / slot organization is huge in terms of code development and maintenance.

    Going in the other direction is harder, since individual UI components generally deal with only a specific part of the document's contents. I try to encapsulate things as much as possible. If a dialog is used to configure a certain set of parameters or properties, then it gets set up with a copy of the specific data class that holds those properties, not a pointer to the document class. As the user makes changes, it emits "changed" signals with a reference to the updated parameter class. In this way, the dialog doesn't know that the parameters came from a document, and it isn't responsible for updating the document. Whoever created the dialog knows where the parameters came from and how to update that with the changes.

    Hope this is useful. I've used this model for several major apps. I keep refining it, but it works well. The serializer concept is particularly useful. I recently had to add support for retrieving parameters from a legacy version of an app. I did not make any changes to the document class at all; all I did was to add new methods to the existing serializers to read and map the old format document into the new document structure.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

  3. #3
    Join Date
    Nov 2019
    Location
    Lyon, France
    Posts
    18
    Thanks
    1
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: A few questions on good practices

    The document class usually holds other class instances as member variables that encapsulate things like inputs, parameters, results, etc.
    So I basically did the opposite, with my other classes having a pointer to the document class. But my document only store important variable that have to be saved, and I generally have two classes for each big ui element: a "displayer" that is fed informations by a "manager" that manage inputs and contains other methods, specific variables and a pointer to the document class.
    I seem to keep a form of encapsulation but not as much as you do, so I may have to rework my code if I have the time.

    The document does not know how to save or load itself. For this, I have a separate set of classes I call "serializers" which know how to save the document and all of its contents to a file.
    Here I also do something similar. the only difference is that I have two big serializer, one to save, and one to load and set the data to the other classes. It may be a better idea to separate them into multiple specific serializer as you did though...

    For this, I have a QFlags structure in the document class, where each individual flag represents a "hint" that some part of the document has changed.
    I did not know about this, I will look into it, seems quite interesting! In short, you only have one signal and let the receivers decide if it concern them or not?

    Thank you very much, this help a lot!

  4. #4
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: A few questions on good practices

    In short, you only have one signal and let the receivers decide if it concern them or not?
    Yes. The receivers decide based on the hints flags.

    Qt Code:
    1. class Document : public QObject
    2. {
    3. Q_OBJECT;
    4.  
    5. public:
    6. using Hint = enum size_t
    7. {
    8. eHintNothingChanged = 0,
    9. eHintThisChanged = 1 << 0,
    10. eHintThatChanged = 1 << 1,
    11. eHintSomethingElseChanged = 1 << 2,
    12. eHintThisChangedToo = 1 << 3,
    13. };
    14.  
    15. signals:
    16. void notifyChange( Document * doc, size_t hints );
    17. // ...
    18. };
    19.  
    20. // Document.cpp
    21.  
    22. void Document::notifyAboutThisAndThat()
    23. {
    24. size_t hints = eHintThisChanged | eHintThatChanged;
    25. emit notifyChange( this, hints );
    26. }
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // SomeOtherClass.h
    2.  
    3. public slots:
    4. void onDocumentChanged( Document * doc, size_t hints )
    5. {
    6. if ( hints & Document::eHintSomethingElseChanged )
    7. {
    8. // It's for me!
    9. }
    10. else
    11. {
    12. // Too bad, not my day
    13. }
    14. }
    To copy to clipboard, switch view to plain text mode 

    "manager" that manage inputs and contains other methods, specific variables and a pointer to the document class.
    My MainWindow class usually has this role. The Document class is usually not connected directly to anything in the UI. The MainWindow holds the pointer to the Document instance and the MainWindow connects to all of the other UI elements and their signals. So for example, if there is a menu item to edit some parameters, the MainWindow retrieves the parameter instance from the document, and sends it to the UI component that edits it. When that component signals that it is done, the MainWindow retrieves the parameters and stores them back in the document. At that point the document sends its signal with the hint "eHintTheParametersChanged".

    I suppose I could use a separate Manager class, but it seems like the Manager class would have to know all about the UI in order to listen for signals, but maybe not. If the MainWindow just creates the UI and then connects the UI signals to slots in the Manager, then the Manager doesn't actually have to know that it was a combo box that sent the signal. I'll have to think about that.

    It may be a better idea to separate them into multiple specific serializer as you did though...
    I think it is easier from a enhancement and maintenance point of view. There is pretty much a 1-1 mapping between a data structure and its serializer. If you change the data structure, you change its serializer at the same time and nothing else is affected. If you add or remove a data structure, then you need to change the serializer code in the "parent" serializer to match the contents of the "parent" data structure. And you bump up the version numbers for whatever serializers are affected by the change so they can change their behavior based on the version number they are reading or writing.
    <=== The Great Pumpkin says ===>
    Please use CODE tags when posting source code so it is more readable. Click "Go Advanced" and then the "#" icon to insert the tags. Paste your code between them.

Similar Threads

  1. QSqlQuery Best Practices?
    By apocrita in forum Newbie
    Replies: 1
    Last Post: 21st June 2019, 10:17
  2. Is it a good practice or good idea to have a stock of actions?
    By remizero in forum General Programming
    Replies: 2
    Last Post: 6th May 2018, 19:36
  3. What are best practices for QSS files?
    By danielvianna in forum Newbie
    Replies: 0
    Last Post: 25th September 2015, 18:26
  4. Good practices for export<->import SQLite datas
    By oscar in forum Qt Programming
    Replies: 1
    Last Post: 8th June 2009, 05:24
  5. Qt <-> Java - Best Practices?
    By mentat in forum Qt Programming
    Replies: 6
    Last Post: 20th July 2006, 03:32

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.