Encapsulation des données

Introduction

Soit la séquence suivante :

1
class Led{
2
 public:
3
    int pinLed;
4
};

Dans cette séquence, la variable pinLed, qui permet de stocker le numéro de port sur lequel la diode est connectée, est toujours accessible par le programmeur (qui n'est pas obligatoire celui qui a créé la classe Led) et l'instruction suivante peut être utilisée dans le programme principal :

1
//Création d'un objet de la classe Led
2
Led led; //"Led" est la classe, "led" est l'objet
3
4
//On accède à la variable membre "pinLed" de l'objet "led"
5
led.pinLed = 112; 

sans que le compilateur ne réagisse, bien qu'il s'agisse d'une erreur (si on suppose bien sûr que le port 112 n'existe pas)

Fondamental

La raison est que nous n'avons pas utilisé le principe de l'encapsulation des données.

  • La plupart du temps, on évitera les déclarations de variables membres en public car cela signifie qu'elles sont visibles pour le programmeur et donc modifiables sans aucun contrôle.

  • Il est possible, en C++, de rendre privées ces variables de manière à ce que l'on ne puisse pas les modifier facilement.

  • C'est ce principe, appelé encapsulation, qui garantit la sécurité des données et empêche leur modification directe.

  • il y a 3 niveaux de protection du contenu d'une classe : privé, protégé et public :

    • private : visible par la classe elle-même mais pas en dehors,

    • protected : visible par la classe elle-même et les classes dérivées (=construites à partir de cette classe)

    • public : visible partout

DéfinitionEncapsulation 

  • l'encapsulation consiste à ne rendre accessibles que certaines méthodes et attributs de la classe en évitant (ou en empêchant) leur accès par un autre moyen que les services proposés (c'est-à-dire des méthodes publiques qui géreront l'accès aux variables et fonctions privées).

  • Ce principe évite à l'utilisateur de la classe de se soucier de son fonctionnement et de faire des erreurs en changeant directement la valeur de certaines données.

  • Cela permet donc de garantir l'intégrité des données contenues dans l'objet.

Complément

  • La structure d'une classe n'a pas obligatoirement à être connue de son utilisateur : l'encapsulation permet alors d'interdire à l'utilisateur de modifier directement les attributs (variables membres), en l'obligeant à utiliser les fonctions membres, appelées interfaces, définies pour les modifier

  • L'intégrité des données est ainsi assurée : par exemple, on peut vérifier que le type des données fournies est correct, ou que les données se trouvent bien dans l'intervalle prévu.

Exemple

Dans l'exemple suivant, l'encapsulation des données n'est pas respectée puisqu'on initialise directement la variable membre pinLed dans la fonction setup() :

1
void setup() {
2
  led.pinLed = 3;
3
}
4

La modification suivante permet de ne plus accéder directement aux données :

1
class Led{
2
  private:
3
  int pinLed;
4
  public:
5
  void init(int value){pinLed = value;};
6
};

et pour le programme principal :

1
Led led;
2
void setup() {
3
  led.init(3);
4
}

Bien entendu, la syntaxe ci-dessous générera une erreur :

1
Led led;
2
void setup() {
3
  led.pinLed = 3;//Cette syntaxe n'est plus possible car l'attribut pinLed est privé !
4
}

ComplémentQuel est, dans notre cas, l'intérêt de l'encapsulation ?

Dans l'exemple précédent, la fonction init() n'a pas un grand intérêt puisqu'elle ne réalise aucun contrôle. Cependant, si on sait que la LED ne peut être connectée à certains bits de port, on pourra ajouter un contrôle au sein de la fonction comme dans l'exemple ci-dessous :

1
#define ERROR_PIN 3
2
#define MAX_PIN 7
3
#define MIN_PIN 4
4
class Led{
5
  private:
6
  int pinLed;
7
  public:
8
  void init(int value){
9
		if(value>= MIN_PIN && value <= MAX_PIN) {pinLed = value;}
10
		else {pinLed = ERROR_PIN;}
11
		pinMode(pinLed, OUTPUT);
12
	};
13
};
14

Ainsi, si on tente d'initialiser le port de la LED à une valeur erronée, c'est la valeur par défaut qui sera imposée (on pourrait également générer un message d'erreur) :

1
Led led;
2
void setup() {
3
  led.init(3);
4
}