finish first part of software architecture
This commit is contained in:
parent
5c5aba4871
commit
eda9fc8abd
1 changed files with 203 additions and 19 deletions
|
@ -1,57 +1,241 @@
|
||||||
---
|
---
|
||||||
title: "PdP: Architecture loficielle"
|
title: "PdP: Architecture logicielle"
|
||||||
date: 2024-03-14
|
date: 2024-03-14
|
||||||
tags: ["besoins", "UML", "developpement logiciel"]
|
tags: ["besoins", "UML", "developpement logiciel"]
|
||||||
categories: ["Projet de programmation", "Cours"]
|
categories: ["Projet de programmation", "Cours"]
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
une architecture logicielle est une structure composite basée sur une
|
||||||
|
décomposition de composants distincts et une ensemble de dépendance et/ou
|
||||||
|
d'interaction entre ces composants.
|
||||||
|
|
||||||
|
La finalité est de répondre un ensemble de besoins logiciels et d'offrir un
|
||||||
|
ensemble de services.
|
||||||
|
|
||||||
|
> Engineering is all about breaking down big problems into smaller ones
|
||||||
|
> and putting the solutions for those problems back together
|
||||||
|
|
||||||
|
Boswell and Fouchet, 2011
|
||||||
|
|
||||||
|
Il est donc question ici de maitriser la taille et la complexité pour assurer le
|
||||||
|
**passage à l'échelle** : décomposer pour maîtriser et faire évoluer. En effet
|
||||||
|
les architectures logicielles doivent aussi être modifiables, extensible et
|
||||||
|
évolutives. Les composants et leurs dépendances doivent **permetttre et
|
||||||
|
faciliter le changement**.
|
||||||
|
|
||||||
|
## Dépendances et composants
|
||||||
|
|
||||||
|
Un composant, aussi appelé aussi *bloc de construction* est tout élément
|
||||||
|
syntaxique qui participe à la structure de son architecture : les intructions,
|
||||||
|
les expressions, fonctions, classes, modules, paquetages, etc.
|
||||||
|
|
||||||
|
Un composant \\(C_1\\) est dépendant de \\(C_2\\) si \\(C_1\\) utilise \\(C_2\\)
|
||||||
|
dans sa définition ou implémentation.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Voici comment représenter les dépendances entre deux classes, notez le sens de
|
||||||
|
la flèche, elle pointe vers \\(C_2\\) : *"dépends de".
|
||||||
|
|
||||||
|
Nous avons trois grande famille de dépendances:
|
||||||
|
|
||||||
|
* Les **utilisations simples** d'éléments de composants. Par exemple une
|
||||||
|
fonction \\(f_1\\) appelle une autre fonction \\(f_2\\);
|
||||||
|
* Les **associations** entre composants par imbrications. Par exemple une
|
||||||
|
classe \\(C_1\\) inclut un attribut de la classe \\(C_2\\);
|
||||||
|
* Les **inclusions** ou **héritages** entre composants par réutilisation. Par
|
||||||
|
exemple un sources C \\(s_1\\) inclut via `#include` un autre fichier
|
||||||
|
\\(s_2\\).
|
||||||
|
|
||||||
|
### Taille -- flux et débit -- des dépendances
|
||||||
|
|
||||||
|
Les dépendances d'une architecture doivent être utilisable, utilisées et utiles.
|
||||||
|
Elles doivent transmettre que le stricte minimum d'informations et de qualité
|
||||||
|
suffisante (contrôle et maîtrise).
|
||||||
|
|
||||||
|
La **taille** d'une dépendance entre \\(c_{1}\\) et \\(c_{2}\\) reflète la
|
||||||
|
quantité d'information qu'elle peut faire transiter ce qui inclus:
|
||||||
|
|
||||||
|
* Le nombre d'éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\);
|
||||||
|
* Le volume des éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\);
|
||||||
|
* La complexité des éléments distincts transmis à \\(c_{1}\\) par \\(c_{2}\\).
|
||||||
|
|
||||||
|
Cette mesure n'as pas vocation être précise.
|
||||||
|
|
||||||
|
Dasn une architecture logicielle, il faut favoriser la définition d'une
|
||||||
|
dependance **avec une taille minimale**
|
||||||
|
|
||||||
|
### Diffusion des changements
|
||||||
|
|
||||||
|
Une architecture où les changements dasn les composants sont diffusés aux
|
||||||
|
autres composants n'est qu'une *architecture factice*.
|
||||||
|
|
||||||
|
Nous devons donc introduire la notion de **force d'une dépendance** entre deux
|
||||||
|
composants \\(c_{1}\\) et \\(c_{2}\\):
|
||||||
|
|
||||||
|
* Elle est **forte** si une modification dans \\(c_{2}\\) entraine une
|
||||||
|
modification dans \\(c_{1}\\).
|
||||||
|
* Elle est faible dans le cas contraire.
|
||||||
|
|
||||||
|
La diffusion des changement va toujours dans le *sens inverse* des dépendances.
|
||||||
|
|
||||||
|
Dans une architecture logicielle, il faut limiter les dépendances forte pour les
|
||||||
|
changements les plus probables dans cette architecture. On est obligé de se
|
||||||
|
concentrer sur les changements les plus probables (on ne pourra considérer tous
|
||||||
|
les changements). Il faut identifier ces changement possibles par le contexte,
|
||||||
|
les besoins non fonctionnels, l'expérience, etc.
|
||||||
|
|
||||||
|
Plusieurs techniques d'implémentation sont disponible pour appliquer le principe
|
||||||
|
de diffusion minimisée des changements comme:
|
||||||
|
|
||||||
|
* Les mécanismes de cloisonnement des langages (expression du privée / public);
|
||||||
|
* En appliquant le principe de *taille minimisé*;
|
||||||
|
* En utilisant les moyens d'expression d'abstration des langages comme *les
|
||||||
|
interfaces*.
|
||||||
|
|
||||||
### Dépendances et utilisation des interfaces
|
### Dépendances et utilisation des interfaces
|
||||||
|
|
||||||
spécification d'un composant -> description abstraite et rigoureuse de ses
|
La spécification d'un composant représente la description abstraite et rigoureuse de
|
||||||
qualités, de sa manière de se comporter, des services qu'il est censé rendre.
|
ses qualités, de sa manière de se comporter, des services qu'il est censé
|
||||||
Un composant qui satisfait une spécification S est dit réaliser S
|
rendre. Un composant qui satisfait une spécification \\(S\\) est dit *réaliser*
|
||||||
|
\\(S\\)
|
||||||
|
|
||||||
Utilisation de l'interface au lieu d'une classe
|
Utilisation de l'interface au lieu d'une classe
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
class C2 {
|
class C2 {
|
||||||
IC1 obj;
|
IC1 obj;
|
||||||
// ...
|
// This is a simple class
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IC1 {
|
interface IC1 {
|
||||||
|
// Interface - kind of specification
|
||||||
}
|
}
|
||||||
|
|
||||||
class C1 implement IC1 {
|
class C1 implement IC1 {
|
||||||
// ...
|
// then iplementation.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
Ainsi `C2` ne dépends pas de `C1` mais de l'interface `IC1`. En plus de décrire
|
Ainsi `C2` ne dépends pas de `C1` mais de l'interface `IC1`. En plus de décrire
|
||||||
abstraitement les composants à implémenter, elles servent d'intermédiaire dans
|
abstraitement les composants à implémenter, elles servent d'intermédiaire dans
|
||||||
les dépendances.
|
les dépendances.
|
||||||
|
|
||||||
|
Les interfaces contrôlent une part des dépendances fortes, on y spécifie les
|
||||||
|
éléments les moins susceptibles de changer. Les interfaces ont deux rôles
|
||||||
|
important dans une architecture logicielle:
|
||||||
|
|
||||||
|
* Décrire de manière abstraite les composants à implémenter;
|
||||||
|
* Servir d'intermédiaire dans les dépendances et ainsi contrôler la force ou la
|
||||||
|
faiblesse des dépendances.
|
||||||
|
|
||||||
|
#### Principe d'inversion des dépendances.
|
||||||
|
|
||||||
|
**Une dependance implicite** est une dépendance qui ne se voit pas directement
|
||||||
|
dans le source d'un programme pas ses appels et ses annotations. Elle est
|
||||||
|
toujours forte par rapport au changement qui la détermine.
|
||||||
|
|
||||||
> Dans une architecture logicielle, il est préférable de faire dépendre les
|
> Dans une architecture logicielle, il est préférable de faire dépendre les
|
||||||
> ocmposants d'interfaces et non les composants implémentés.
|
> composants d'interfaces et non les composants implémentés.
|
||||||
|
|
||||||
Principe d'inversion des dépendances.
|
si nous reprenons le schéma précédent, alors \\(IC_{1}\\) a été écrite par
|
||||||
|
rapport à l'usage de \\(C_{2}\\) par \\(C_{1}\\), nous pouvons alors en déduire
|
||||||
|
que \\(C_{2}\\) dépends de la paire \\((C_{1},IC_{1})\\). C'est notre
|
||||||
|
**inversion de dépendance**
|
||||||
|
|
||||||
Une dependance implicite -> qui ne se voit pas directement dans le source d;un
|
### Notion étendue des dépendances
|
||||||
programme pas ses appels et ses annotations. Elle est toujours forte par rapport
|
|
||||||
au chamgement qui la détermine.
|
Nous avons vu des *dépendances explicites* qui se matérialise par les appels de
|
||||||
|
méthodes, les classes etc. Mais il existe aussi les **dépendances implicite**:
|
||||||
|
c'est une dépendance qui ne se voit pas directement dans le code source.
|
||||||
|
|
||||||
Elles peuvent prendre des formes très différentes des dépendances explicite par
|
Elles peuvent prendre des formes très différentes des dépendances explicite par
|
||||||
|
|
||||||
* la duplication de code
|
* La duplication de code;
|
||||||
* les similarité de services
|
* les similarité de services offerts par des composants distincts;
|
||||||
* <cf slide>
|
* les similarité de structure entre composants distincts.
|
||||||
|
|
||||||
|
Les dépendances implicites sont toujours des dépendances fortes car se sont les
|
||||||
|
changements qui les définissent. Il est donc préférable de minimiser leur
|
||||||
|
apparition. (on minimise alors la duplication de code et les similarité par
|
||||||
|
des techniques de factorisation de code)
|
||||||
|
|
||||||
LEs archilogicielles sont généralement des structures hiérarchisées.
|
|
||||||
|
|
||||||
## Granularité de structure, cohésion et couplage
|
## Granularité de structure, cohésion et couplage
|
||||||
|
|
||||||
niveau de granularité défini par
|
Les architecture logicielles sont généralement des structures hiérarchisées de
|
||||||
|
composants, c'est une conséquence naturelle du *décomposer pour maîtriser*.
|
||||||
|
Cette hiérarchisation est généralement simplifiée car elle peut être constituée
|
||||||
|
essentiellement de niveaux homogène de structure de composants.
|
||||||
|
|
||||||
Gq et G2 deux niveau de granilarité alirs couplage : ensemble des dépendances
|
Son niveau de granularité est déterminé par des composants de même espèce
|
||||||
entre les composants de G2
|
capable d'inclure des composants du niveau inférieur (s'il y en a). Les niveaux
|
||||||
|
habituels de granularité offerts par les langages de programmation sont:
|
||||||
|
|
||||||
|
* Les valeurs, données, instructions;
|
||||||
|
* Les variables, attributs, fonctions (ou méthodes);
|
||||||
|
* Les modules, classes, objets,
|
||||||
|
* Les paquetages, clusters, namespace
|
||||||
|
* Les super-paquetages;
|
||||||
|
* Les couches.
|
||||||
|
|
||||||
|
Pour des raison de simplification et d'intelligibilité, les dépendances peuvent
|
||||||
|
se restreindre à ne s'énoncer qu'au sein de chaque niveau de granularité : un
|
||||||
|
composant \\(C1\\) est explicitement dépendant de \\(C2\\) si \\(C1\\) utilise
|
||||||
|
\\(C2\\) dans sa définition ou son implémentation et si tous les deux sont au
|
||||||
|
même niveau de granularité. Les dépendances sont donc à considérer entre
|
||||||
|
fonctions, entre classes, entre paquetages, etc.
|
||||||
|
|
||||||
|
### Graphes de dépendances
|
||||||
|
|
||||||
|
Les dépendances dans une granularité fixée induisent des **graphes** dont les
|
||||||
|
sommets sont d'une seule espèce de composants et les arcs des dépendances
|
||||||
|
explicites.
|
||||||
|
|
||||||
|
Dans la mesure du possible, il faut favoriser la construction de niveau de
|
||||||
|
granularité homogènes donnant lieu à des graphe de dépendances distincts. C'est
|
||||||
|
un principe de simplification et d'intelligibilité des architectures logicielles
|
||||||
|
mais qui n;est pas applicable tout le temps (et c'est parfois souhaitable). Les
|
||||||
|
langages permettent souvent des moyens de déroger à ce principe (*nested
|
||||||
|
classes* en Java, lambda expression ...).
|
||||||
|
|
||||||
|
### Suites de granularités, consécutivité
|
||||||
|
|
||||||
|
Les granularités peuvent donc former des suites constitués des éléments que nous
|
||||||
|
avons évoqué plus haut (données, variables, fonctions etc.). Deux **granularité
|
||||||
|
consécutives** sont deux granularité qui se suivent dans cette liste, elles
|
||||||
|
engendre des **graphes consécutifs de dépendances**.
|
||||||
|
|
||||||
|
Il faut veiller à ce que chaque graphes de consécutivité soit plus simple que le
|
||||||
|
précédent, ils dessinent alors **une pyramide**. Chaque niveaux est une
|
||||||
|
opportunité de simplifier, synthétiser la description d'une architecture. Bien
|
||||||
|
entendu il existe des contre-exemples, comme **les classes qui ne contiennent
|
||||||
|
qu'une seule méthode**.
|
||||||
|
|
||||||
|
Il serait naturel de regrouper un ensemble de méthodes très dépendantes en elles
|
||||||
|
dans une seule classe, ou encore des classes très dépendances dans un
|
||||||
|
paquetage. C'est le principe **de simplification min-max**: il faut en sorte
|
||||||
|
que les composants du niveau de granularité \\(G2\\) englobent le **maximum** de
|
||||||
|
dépendances de \\(G1\\) tel qu'il reste un **minimum** de dépendances en
|
||||||
|
composants de \\(G2\\).
|
||||||
|
|
||||||
|
### Couplage et cohésion
|
||||||
|
|
||||||
|
Le couplage représente l'ensemble des dépendances entre composants de \\(G2\\),
|
||||||
|
la cohésion celles au sein de chacun des composants de \\(G2\\) entre les
|
||||||
|
éléments de \\(G1\\).
|
||||||
|
|
||||||
|
Si \\(G2\\) est un ensemble de classes et \\(G1\\) un ensemble d'attributs et de
|
||||||
|
méthodes de ces classes alors:
|
||||||
|
|
||||||
|
* le couplage correspond à l'ensemble des dépendances entre classe de \\(G2\\);
|
||||||
|
* la cohésion définie pour chaque classe de \\(G2\\) correspond à l'ensemble
|
||||||
|
des dépendances entre ses attributs et méthodes.
|
||||||
|
|
||||||
|
Pour reprendre notre **principe min-max**: dans une architecture logicielle, pour
|
||||||
|
toute paire de niveau de granularité, il faut privilégier à la fois **la
|
||||||
|
cohésion la plus forte** et **le couplage le plus faible**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue