Programmation Orientée Objets en embarqué

Nous commençons une série d’articles qui présentent la mise en œuvre d’une approche par objets en programmation embarquée.

Le but de ce premier article est de présenter succinctement les concepts indispensables à la compréhension des programmes que nous allons réaliser. Une fois ces notions présentées, nous effectuerons une présentation du cahier des charges et une approche par objets, puis nous « réaliserons » chaque objet pour remplir le cahier des charges.

Le sujet choisi est la réalisation d’un chenillard à led avec une carte Nano Pi ou Raspberry Pi en langage C++. Comme nous utiliserons la librairie piduino dont l’API est compatible avec Arduino, nous pourrons aussi effectuer ses activités avec une carte Arduino (avec quelques adaptations qui seront signalées).

On parle souvent de POO pour Programmation Orientée Objet. Je préfère le concept d’approche par objets car cette problématique ne concerne pas que la programmation, elle concerne toute la démarche de projet. D’ailleurs ont pourra noter que le langage SysML est une extension pluri-technologique du langage UML initialement utilisé en informatique. L’informatique a donc permis de faire évoluer d’ingénierie d’une façon générale.

Cette série d’articles suppose que vous ayez quelques connaissances en C. Les particularités de C++ pourront être approfondies par la lecture du cours C++ fait par Henri Garreta. Pour ceux qui ont un peu plus de temps et qui souhaitent une approche plus « tuto », je vous conseille le cours C++ sur OpenClassroom.

Alors que ces cours se concentrent sur l’apprentissage du langage, nous, nous allons nous concentrer sur la méthode d’approche par objets. Comme nous souhaitons que la méthode soit translatable sur n’importe quelle plateforme ou langage, nous introduirons l’utilisation du langage de modélisation graphique UML.

Cette série d’articles s’adresse à des débutants qui ont déjà réalisé des programmes dans un contexte de « Faire soi-même » (DIY) sur une carte Arduino, Raspberry Pi, Nano Pi Pi … et qui souhaitent améliorer leurs méthodes de programmation. Ces articles pourront aussi être utiles à des étudiants en sciences du numérique souhaitant mettre en œuvre rapidement cette démarche sur un cas pratique.

 Qu’est-ce que l’approche par objets ?

L’approche par objets vise à décrire un problème en identifiant les objets qui le compose.

Les objets seront décrits en terme de fonctionnalités et de propriétés. L’approche par objets ne doit pas être confondue avec l’utilisation d’un langage objets (comme C++, Java …) car on peut très bien utiliser un langage objets sans jamais faire d’approche par objets. Beaucoup de débutants en C++ pensent qu’il suffit d’utiliser ce langage pour faire de l’objet, ce n’est pas le cas.

Bien évidement, l’utilisation d’un langage objets est préférable dans une approche par objets ! Ils ont juste été inventé pour ça… On peut noter qu’il existe des projets utilisant l’approche par objets qui n’utilisent pas de langage objet, le plus connus est la boite à outils GTK qui est développée en langage C. GTK est utilisée par exemple dans le projet GNOME.

Le but de cette méthode est d’organiser la programmation afin de la rendre plus efficace, plus solide et plus évolutive.

Le but est de décrire un programme avec des concepts qui se rapprochent du concret. Dans notre quotidien nous manipulons des objets « réels » en permanence, nous utiliserons souvent ce parallèle avec la réalité pour expliquer des concepts « abstraits » de la POO.

Concepts de base de l’approche par objets

 L’encapsulation

L’approche par objets repose sur le principe d’encapsulation, on parle aussi de concept de « boîtes noires », c’est à dire, qu’on va décrire l’interface d’un objet qui sera accessible du public, la programmation en elle-même (on parle de l’implémentation) sera cachée du public (elle est privée, private en anglais). Un langage objets disposera donc de ce concept sous forme de mots clés, en C++ c’est public et private.

L’héritage

Le principe est que, sauf correction de bug ou d’erreur de conception, on ne modifie pas l’implémentation d’un objet. Il faut donc disposer d’un mécanisme qui va permettre d’étendre les fonctionnalités d’un objet. Prenons un exemple concret, un vélo électrique est un vélo, on s’attachera donc à modéliser un vélo, puis on créera un nouveau modèle de vélo, un vélo électrique qui est dérivé de l’objet initial. Ce mécanisme s’appelle l’héritage. Si un programme est prévu pour utiliser un vélo, il pourra quand même utiliser un vélo électrique dans ses fonctions de base (celles définies pour l’objet vélo).

 La classe

Un autre concept essentiel à comprendre en programmation orientée objet est le concept de classe. Une classe permet de modéliser un objet en décrivant ses fonctionnalités, on parle de méthodes, et ses propriétés qui sont des valeurs que l’on peut lire et/ou modifier. Les méthodes et les propriétés sont rattachés à une classe, on dit qu’ils sont les membres de la classe. On peut aussi utiliser le terme fonction membre pour désigner une méthode.

En C++, il y a 2 mots clés pour définir une classe : class et struct. Ceux qui connaissent le langage C auront notés que struct est utilisé dans ce langage pour décrire les structures (de données uniquement en C). Cela montre que le concept de classe est une extension aux fonctions de la notion de structure. Tous les membres d’une classe définie avec struct sont par défaut publics, alors que ceux d’une classe définie avec class sont par défaut privés. Un bon principe pour respecter l’encapsulation est donc de se limiter à n’utiliser que le mot clé class. C’est ce que nous ferons dans ces articles.

La classe est une description statique d’un objet. Un objet est la « concrétisation » dynamique d’une classe, on dit qu’un objet est une instance de classe, dans les faits un objet est l’association d’une classe et d’un bloc de variables stockées en mémoire vive (RAM donc…).

 Constructeur et destructeur de classe

Comme un objet est l’association d’une classe et d’un blocs de variables, il faut bien initialiser ces variables afin de mettre l’objet dans un état connu. C’est le rôle du constructeur de classe. Il s’agit d’une fonction membre particulière, chargée d’initialiser ses variables.

Parfois, le constructeur va « consommer » des ressources de l’ordinateur (mémoire dynamique, entrées-sorties …), il faut donc prévoir un mécanisme de libération de ses ressources, c’est le rôle du destructeur de classe.

En C++, les constructeurs sont des fonctions membres qui portent le même nom que la classe et ne renvoient aucun paramètre (même pas void), le destructeur est une fonction membre qui portent le même nom que la classe mais précédé du caractère ~ et ne renvoient aucun paramètre.

Le polymorphisme

Implicitement, je viens de dire dans la phrase précédente qu’il peut exister plusieurs constructeurs de classe, c’est vrai, mais cela ne concerne pas que les constructeurs. Toutes les fonctions membres, sauf le destructeur, peuvent avoir plusieurs « versions » ou « formes », on parle de polymorphisme ou surcharge. on pourra par exemple définir une fonction d’écriture write(...) qui prend un paramètre de type entier et une autre qui porte le même nom et qui prend un paramètre flottant. Comme le compilateur choisira automatiquement la « version » à utiliser, il faut que deux fonctions « surchargées » prennent des paramètres de nature différentes.

Voilà nous sommes arrivés à la fin de ce premier article, dans le prochain nous passerons à la pratique.