1

yop,

Je suis en train de faire un programme qui process des données à haute dose. Je fais ça en C++, et j'ai du mal à organiser mes données pour ne pas violer le principe d'encapsulation, tout en gardant une certaine efficacité (le traitement des données peut devenir lourd s'il est fait avec les pieds).

Le processus de traitement des données :
- d'abord un thread part d'une list de strings, et pond des objets en fonction (pré-processing)
- ensuite, une fenêtre affiche le résultat du pré-traitement, et demande une confirmation avant de passer à la suite
- enfin, un autre thread utilise les données calculées par le premier, pour effectuer des opérations

Les threads ne sont là que pour garder une UI réactive (bouton cancel, barre de progression etc) quand les opérations se déroulent. Ce n'est pas central au problème.

J'ai pensé à deux façon de faire :

1.
Le premier thread prend en argument la liste de string (c'est pas lourd), et crée des objets issus de ses calculs. Facile, mais il se retrouve propriétaire d'objets dont il n'a plus rien à faire à la fin.
Comment les passer élégamment au dialogue de confirmation sans se taper des copies énormes, vu qu'il est propriétaire des données calculées ?
Le problème se pose de la même façon pour passer les données au second thread, beaucoup plus lourd en terme de ressources utilisées.
Ca me parait donc hyper lourd comme solution

2.
Un objet "Data" (singleton) est initialisé avant le démarrage du thread de pré-processing, avec la liste de strings.
Le thread de pré-processing récupère les strings (ça va encore comme passage de données), puis fait un taf dessus. Mais les données calculées doivent réintégrer l'objet Data, ça fait énormément de duplications.
Solution : l'objet Data peut renvoyer des pointeurs sur ses données élémentaires (des objets DataPart), ce qui évite toute copie inutile et accélère grandement les choses, réduit les appels de méthodes etc...
Mais il expose ainsi ses données, violant le principe d'encapsulation.

Question : comment bien faire un design dans ce type de cas ?

Merci d'avance !

2

C'est toujours un peu difficile de répondre dans le vague mais la mémoire étant partagée entre les threads et les threads étant capables d'appeler des méthodes sur des instances communes je ne suis pas sûr de voir le problème ? Pourquoi serais-tu obligé de copier des données ?
En gros ton premier thread fournisseur de données initialise les objets métier à partir de tes strings et ensuite ton thread métier fait ce qu'il veut avec ? (Et pour passer les objets métier du fournisseur au consommateur tu utilises par exemple une file quelconque avec un accès synchronisé)

3

Oui c'est ça l'idée. Le problème n'est pas dû au modèle threadé.
Le problème vient que je devrais passer des données traitées/à traiter d'un objet à un autre.
Du coup il vaut mieux externaliser les données (singleton) qui sont utilisées par tout le monde.

MAIS

il est évident qu'un objet qui bosse sur ses propres données fait ça de façon 1000 fois plus efficace que quand il faut passer par l'API d'un objet externe.

Donc actuellement j'ai le choix entre faire des gros transferts de données, ou demander à l'objet Data d'exposer ses données au maximum pour ne pas trop perdre en efficacité.

(on serait en C, Data serait une simple structure à laquelle tout le monde accéderait sans aucun complexe, ça serait beaucoup plus simple ^^)

4

Pour garder un peu d'encapsulation tout en restant aussi efficace que des accès directs à des structs C (à supposer que tu compiles avec optimisation, bien sûr ^^), des getters et setters inline (tu connais déjà certainement, les déclarer dans la section public du corps de la classe Data est une des façons d'y parvenir) te conviendraient-ils ?
avatar
Membre de la TI-Chess Team.
Co-mainteneur de GCC4TI (documentation en ligne de GCC4TI), TIEmu et TILP.
Co-admin de TI-Planet.

5

>Le problème vient que je devrais passer des données traitées/à traiter d'un objet à un autre.
Avec des pointeurs ou références je vous pas en quoi c'est un problème de performances ?

>Du coup il vaut mieux externaliser les données (singleton) qui sont utilisées par tout le monde.
J'aurais pas spécialement vu un singleton mais admettons.
En gros tu peux organiser ton logiciel en différentes couches, typiquement un empilement de
Présentation/UI
Application
Business/métier
Data Access /persistance

Chaque couche n'accèdant qu'à la couche inférieure directe.
Et tu peux ajouter une couche transverse Attributs qui porte juste les données si tu veux éviter trop de transformations.

>(on serait en C, Data serait une simple structure à laquelle tout le monde accéderait sans aucun complexe, ça serait beaucoup plus simple ^^)
Bah voilà, c'est pas parce que c'est du c++ que c'est différent : si les données sont partagées autant les rendre visibles et puis c'est tout. Enfin c'est mon avis.
(C'est la données du projet, pas les données internes d'une classe)

6

Pen^2 (./5) :
(C'est la données du projet, pas les données internes d'une classe)
A h tiens, intéressant comme point de vue. C'est pour ça que j'en fait un singleton : ça existe en une instance unique by design, et tout le monde peut y accéder (plusieurs UI différentes, plusieurs threads, des fichiers de conf ou que sais-je encore).

Si le pattern singleton ne te plait pas, tu penses à quoi ?

Merci pour ta proposition de modèle, ça me questionne et c'est bien. J'ai besoin d'idées externes au lieu de toujours pousser à bou mes propres concepts, qui ne sont certainement pas les meilleurs...

Merci encore !

7

On parle de quelles quantités de données ? Parce que j'ai le pressentiment que comme souvent, tu te fais des nœuds au cerveau pour pas grand-chose ^^
avatar
Zeroblog

« Tout homme porte sur l'épaule gauche un singe et, sur l'épaule droite, un perroquet. » — Jean Cocteau
« Moi je cherche plus de logique non plus. C'est surement pour cela que j'apprécie les Ataris, ils sont aussi logiques que moi ! » — GT Turbo

8

C’est variable, ça peut aller de quelques dizaines de strings à plusieurs dizaines de milliers (+ quelques données qui sont calculées pour chacune).

En fait, si je veux que les process et les différentes parties de l’UI ne s’adressent qu’à l’objet Data, pour respecter l’encapsulation, ça fait des tonnes d’accesseurs pour lire une sous-donnée d’une sous-donnée quelque part dans Data, faire un calcul, puis re-envoyer un résultat à enregistrer, avec à nouveau X méthodes a appeler pour traverser toute la structure de Data.

En plus d’être très inefficace, ça complique beaucoup le code...