Décomposition hiérarchique
Vous ne devriez jamais commencer à écrire des classes pour votre application tout de suite. Il doit d'abord être conçu. La conception doit se terminer par une architecture réfléchie. Et pour obtenir cette architecture, vous devez décomposer systématiquement le système.
La décomposition doit être effectuée de manière hiérarchique - d'abord, le système est divisé en grands modules / sous-systèmes fonctionnels qui décrivent son fonctionnement sous la forme la plus générale. Ensuite, les modules résultants sont analysés plus en détail et divisés en sous-modules ou objets.
Avant de sélectionner des objets, divisez le système en blocs sémantiques de base, au moins mentalement. Dans les petites applications, cela est généralement très facile à faire : quelques niveaux de hiérarchie suffisent amplement, car le système est d'abord divisé en sous-systèmes / packages, et les packages sont divisés en classes.
![Décomposition hiérarchique](https://cdn.codegym.cc/images/article/d5e9aa2b-5783-430f-b5b5-1832bc33c540/800.jpeg)
Cette idée n'est pas aussi triviale qu'il n'y paraît. Par exemple, quelle est l'essence d'un « modèle architectural » aussi commun que Modèle-Vue-Contrôleur (MVC) ?
Il s'agit de séparer la présentation de la logique métier . Tout d'abord, toute application utilisateur est divisée en deux modules - l'un est responsable de la mise en œuvre de la logique métier elle-même (modèle) et le second est responsable de l'interaction avec l'utilisateur (interface utilisateur ou vue).
Ensuite, il s'avère que les modules doivent en quelque sorte interagir, pour cela ils ajoutent un contrôleur, dont la tâche est de gérer l'interaction des modules. Toujours dans la version mobile (classique) de MVC, le modèle Observer y est ajouté afin que la vue puisse recevoir des événements du modèle et modifier les données affichées en temps réel.
Les modules typiques de niveau supérieur, obtenus à la suite de la première division du système en composants les plus grands, sont précisément :
- Logique métier ;
- Interface utilisateur;
- Base de données;
- Système de messagerie ;
- Conteneur d'objets.
La première division divise généralement l'intégralité de l'application en 2 à 7 (maximum 10 parties). Si nous le décomposons en plusieurs parties, nous souhaiterons alors les regrouper et nous obtiendrons à nouveau 2 à 7 modules de niveau supérieur.
Décomposition fonctionnelle
La division en modules/sous-systèmes est mieux faite en fonction des tâches que le système résout . La tâche principale est divisée en ses sous-tâches constitutives, qui peuvent être résolues/exécutées de manière autonome, indépendamment les unes des autres.
Chaque module doit être responsable de la résolution d'une sous-tâche et exécuter sa fonction correspondante . Outre la finalité fonctionnelle, le module se caractérise également par un ensemble de données nécessaires à l'exercice de sa fonction, à savoir :
Module = Fonction + Données nécessaires à son exécution.
Si la décomposition en modules est effectuée correctement, alors l'interaction avec d'autres modules (responsables d'autres fonctions) sera minime. C'est peut-être le cas, mais son absence ne devrait pas être critique pour votre module.
Un module n'est pas un morceau de code arbitraire, mais une unité de programme distincte (sous-programme) fonctionnellement significative et complète qui fournit une solution à une certaine tâche et, idéalement, peut fonctionner indépendamment ou dans un autre environnement et être réutilisée. Le module doit être une sorte « d'intégrité capable d'une relative indépendance dans le comportement et le développement ». (Christophe Alexandre)
Ainsi, la décomposition compétente repose avant tout sur l'analyse des fonctions du système et des données nécessaires à l'exécution de ces fonctions. Les fonctions dans ce cas ne sont pas des fonctions de classe et des modules, car ce ne sont pas des objets. Si vous n'avez que quelques cours dans un module, vous en avez trop fait.
Connectivité forte et faible
Il est très important de ne pas en faire trop avec la modularisation. Si vous donnez à un débutant une application Spring monolithique et lui demandez de la décomposer en modules, il sortira chaque Spring Bean dans un module séparé et considérera que son travail est terminé. Mais ce n'est pas.
Le critère principal de la qualité de la décomposition est la façon dont les modules se concentrent sur la résolution de leurs tâches et sont indépendants.
Ceci est généralement formulé comme suit : "Les modules obtenus à la suite de la décomposition doivent être conjugués au maximum en interne (cohésion interne élevée) et interconnectés au minimum les uns avec les autres (faible couplage externe)."
Cohésion élevée, cohésion élevée ou "cohésion" au sein du module, indique que le module se concentre sur la résolution d'un problème étroit et n'est pas engagé dans l'exécution de fonctions hétérogènes ou de responsabilités non liées.
La cohésion caractérise le degré auquel les tâches effectuées par le module sont liées les unes aux autres.
Une conséquence de la haute cohésion est le principe de responsabilité unique - le premier des cinq principes SOLID , selon lequel tout objet / module ne devrait avoir qu'une seule responsabilité et il ne devrait pas y avoir plus d'une raison de le changer.
Couplage faible , couplage lâche, signifie que les modules dans lesquels le système est divisé doivent être, si possible, indépendants ou couplés de manière lâche les uns aux autres. Ils doivent pouvoir interagir, mais en même temps en savoir le moins possible les uns sur les autres.
Chaque module n'a pas besoin de savoir comment fonctionne l'autre module, dans quelle langue il est écrit et comment il fonctionne. Souvent, pour organiser l'interaction de tels modules, un certain conteneur est utilisé, dans lequel ces modules sont chargés.
Avec une conception appropriée, si vous modifiez un module, vous n'aurez pas à modifier les autres, ou ces modifications seront minimes. Plus le couplage est lâche, plus il est facile d'écrire/comprendre/étendre/réparer le programme.
On pense que des modules bien conçus doivent avoir les propriétés suivantes :
- Intégrité fonctionnelle et exhaustivité - chaque module implémente une fonction, mais l'implémente correctement et complètement, le module effectue indépendamment un ensemble complet d'opérations pour implémenter sa fonction.
- Une entrée et une sortie - à l'entrée, le module de programme reçoit un certain ensemble de données initiales, effectue un traitement significatif et renvoie un ensemble de données de résultat, c'est-à-dire que le principe IPO standard est mis en œuvre - entrée -\u003e processus -\u003e sortir.
- Indépendance logique - le résultat du travail du module de programme ne dépend que des données initiales, mais ne dépend pas du travail des autres modules.
- Liens d'information faibles avec d'autres modules - l'échange d'informations entre les modules doit être minimisé si possible.
Il est très difficile pour un débutant de comprendre comment réduire encore plus la connectivité des modules. En partie, cette connaissance vient avec l'expérience, en partie - après avoir lu des livres intelligents. Mais il est préférable d'analyser les architectures des applications existantes.
Composition au lieu d'héritage
La décomposition compétente est une sorte d'art et une tâche difficile pour la plupart des programmeurs. La simplicité est ici trompeuse et les erreurs coûtent cher.
![](https://cdn.codegym.cc/images/article/d7e785ed-e16d-4354-883c-0ad83327512a/512.jpeg)
Il arrive que des modules dédiés soient fortement couplés les uns aux autres et ne puissent être développés indépendamment. Ou il n'est pas clair de quelle fonction chacun d'eux est responsable. Si vous rencontrez un problème similaire, il est fort probable que le partitionnement en modules n'ait pas été effectué correctement.
Le rôle de chaque module doit toujours être clair . Le critère le plus fiable pour que la décomposition soit effectuée correctement est de savoir si les modules sont des sous-programmes indépendants et précieux qui peuvent être utilisés isolément du reste de l'application (et peuvent donc être réutilisés).
Lors de la décomposition d'un système, il est souhaitable de vérifier sa qualité en se posant les questions : "Quelle tâche réalise chaque module ?", "Est-il facile de tester les modules ?", "Est-il possible d'utiliser les modules seuls ou dans un autre environnement?" affecter les autres?"
Vous devez essayer de garder les modules aussi autonomes que possible . Comme mentionné précédemment, il s'agit d'un paramètre clé pour une décomposition correcte . Elle doit donc être réalisée de manière à ce que les modules soient initialement faiblement dépendants les uns des autres. Si vous avez réussi, alors vous êtes grand.
Sinon, tout n'est pas perdu ici non plus. Il existe un certain nombre de techniques et de modèles spéciaux qui vous permettent de minimiser et d'affaiblir davantage les liens entre les sous-systèmes. Par exemple, dans le cas de MVC, le pattern Observer a été utilisé à cet effet, mais d'autres solutions sont possibles.
On peut dire que les techniques de découplage constituent la principale "boîte à outils de l'architecte". Il suffit de comprendre que nous parlons de tous les sous-systèmes et qu'il faut affaiblir la connexion à tous les niveaux de la hiérarchie , c'est-à-dire non seulement entre les classes, mais aussi entre les modules à chaque niveau hiérarchique.