Conseils de conception objets

a marqué ce sujet comme résolu.

Bonjour,

Dans le cadre d’une application professionnelle, j’essaye de concevoir une application orientée objet en C++14 dans la mesure du possible.

Ayant plus l’habitude d’un C procédural ou C objet très limité, je voudrais vérifier que je ne peux pas mieux faire dans la conception de l’application actuelle.

En gros c’est une application qui collecte via différents capteurs des données sur une carte électronique embarquée et envoie régulièrement les données par MQTT sur un serveur distant.

Résultant j’ai une classe principale avec une boucle infinie qui s’occupe de gérer la séquence principale et de faire dialoguer au maximum les composants entre eux (en gros récolter les données des capteurs, les injecter dans des variables pour ensuite les sauvegarder ou les envoyer avec au milieu des opérations sur le matériel).

Cependant l’application est configurée par un gros fichier XML. Résultat j’ai une classe importante qui lit ce fichier pour ensuite remplir des structures de données exploitables. Cette classe peut générer des objets lui même à partir de ces éléments, et la classe principale peut aussi récupérer ces données pour les injecter dans d’autres objets (globalement presque tous les objets du logiciels sont liés à ces paramètres, mais la quasi totalité n’attendent qu’une structure de donnée dans leur constructeur pour les utiliser). Le but est de circoncire l’usage de cette classe qu’à ces deux endroits. Mais je ne sais pas s’il y a mieux pour gérer la problématique de la configuration.

Ensuite, et je pense que c’est mon principal soucis, j’ai globalement deux classes en accès global. L’un lié au matériel (qui possède les accès vers les GPIO, les LEDs ou les lignes séries), l’autre au MQTT. En effet, le matériel ou le MQTT peuvent servir à récupérer des informations utiles d’une part, mais surtout à en envoyer d’autres comme des erreurs ou des données. Si j’essaye d’utiliser au maximum la classe principale pour gérer cela, ce n’est pas toujours possible.

Je sens que c’est un peu bancal comme idée mais j’ai beau chercher (sur Internet ou dans mon cerveau), je ne parviens pas à une meilleure conception pour éviter ces appels.

Pour que ce soit plus clair, je vais donner le niveau d’abstraction de ces objets.

La classe "Matériel" initialise le matériel et "le découvre". En gros il possède plusieurs map qui fait le lien entre une fonction et une instance du matériel. Suivant le type de matériel, les objets clients peuvent manipuler le matériel à travers celui-ci quand c’est simple (genre si je veux allumer la LED des erreurs, le client appelle une méthode du genre Hardware::setGPIO(LED_ERROR, true), LED_ERROR étant un nombre énuméré. Le client ne sait pas ce qu’est la classe Led en l’occurrence et ne sait d’eux que les énumérations liées à leur fonction pour savoir laquelle utiliser.

La classe MQTT gère la connexion / déconnexion / souscription / reconnection / envoie d’un message. Après un thread interne se charge d’envoyer les données quand la connexion est établie ou de sauvegarder les données en attendant si ce n’est pas le cas. Globalement les clients n’utilisent que la fonction pour envoyer un nouveau message (en gros un topic de destination + un message).

Voilà, je connais les principes SOLID, les design pattern et tout mais c’est souvent assez abstrait et si cela peut mettre en évidence un défaut de conception, cela n’apporte pas forcément une solution simple pour y remédier. En l’occurrence je sens que mon architecture est améliorable mais j’ignore comment. Si vous avez des idées, je suis preneur.

Merci.

+0 -0

Lu’!

Je suis pas un cador pour les gros systèmes. Mais il y a un truc qui me chagrine pour tes deux classes en accès global.

Typiquement pour la partie matérielle, j’ai l’impression qu’un objet en tant que tel est ce que tu cherches. J’ai plutôt l’impression que ce qu’on voudrait c’est des fonctions libres. Si je ne me trompe pas, cette partie ne maintient pas réellement d’autres données que les positions mémoires auxquelles écrire, donc je ne vois pas de risque de péter un invariant en tant que tel. Finalement, je pense que ce dont tu as besoin ici c’est d’une abstraction de ton matériel, qui ne nécessite pas forcément de penser objet.

Si je ne me trompe pas, cette partie ne maintient pas réellement d’autres données que les positions mémoires auxquelles écrire

Même pas, ça c’est le but du noyau Linux de le faire. Par exemple, pour lire une valeur du GPIO, je dois lire le contenu du fichier /sys/class/gpio/gpioXX/value, XX étant une valeur liée au matériel réel. La classe "Matériel" maintient des map qui permettent aux autres objets de faire des requêtes par rapport à la fonctionnalité désirée (par exemple, la GPIO qui détecte un court circuit, peut importe où il est matériellement car cela peut changer suivant la révision de la carte).

De manière très similaire pour les LED ou ports séries. C’est vrai qu’en soit détruire cette classe pour les utiliser en tant que fonction libre ne serait pas un changement fondamental. Mais est-ce pertinent / utile de faire ainsi ?

Par contre pour la classe MQTT, je trouve que l’objet s’y prête bien car en son sein il y a le client MQTT qui représente la connexion avec le serveur. Le mettre en fonction libre ne permettrait pas d’exploiter cela convenablement à mon avis.

Finalement, je pense que ce dont tu as besoin ici c’est d’une abstraction de ton matériel, qui ne nécessite pas forcément de penser objet.

Le noyau fourni déjà une belle abstraction, et à part découpler la fonction avec une partie matérielle précise (ce que fait en l’occurrence ma classe matérielle j’ai l’impression), je ne vois pas comment faire plus en ce sens. Une idée ?

+0 -0

Tu peux écrire des classes proxy pour les différents composants. Genre

1
2
3
4
5
6
auto led = Hardware::getGPIO(Leds::LED_ERROR); 
led.on(); 
led.off();

auto serial1 = Hardware::getSerial(Serials::Serial1); 
serial1.write("coucou"); 

de sorte que chaque client ne dépend que des composants dont il a besoin et pas de Hardware. Ca peut aussi faciliter la création de "méta composants" (genre un capteur qui dispose d’un paramètre TOR et d’une sortie analogique).

Au passage, Une série de posts sur le C++14 pour l’embarqué .

J’ai pas compris où était localisé le MQTT. Mais ca me dérangerait qu’il soit dispatché aux différents clients. C’est un moyen, pas une fonctionnalité. Il faut que tes clients manipulent quelque chose qui fait transparaître l’objectif de faire remonter de l’information. Que ca soit transféré par MQTT, écrit sur carte SD ou port série, il s’en fiche.

+0 -0

de sorte que chaque client ne dépends que des composants dont il a besoin et pas de Hardware. Ca peut aussi faciliter la création de "méta composants" (genre un capteur qui dispose d’un paramètre TOR et d’une sortie analogique).

Ok je vois, cela rejoint +/- ce que disait Ksass. Cela me semble bien.

Au passage, Une série de posts sur le C++14 pour l’embarqué .

Merci pour la lecture, j’ai lu un peu et je vois un peu où il veut en venir (même s’il est plus bas niveau que mon application actuelle).

J’ai pas compris où était localisé le MQTT. Mais ca me dérangerait qu’il soit dispatché aux différents clients. C’est un moyen, pas une fonctionnalité. Il faut que tes clients manipulent quelque chose qui fait transparaître l’objectif de faire remonter de l’information. Que ca soit transféré par MQTT, écrit sur carte SD ou port série, il s’en fiche.

Ça me gênait aussi, sans savoir comment le résoudre. Je sens que la seule méthode qui reste est comme tu dis, remonter l’information jusqu’à l’endroit où l’usage de la classe MQTT (ou éventuellement d’un autre moyen de communication) fait sens.

Merci pour vos avis, je vois un peu plus clair.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte