La refactorisation est définie comme le processus de modification d'un système logiciel de telle sorte qu'il n'altère pas le comportement externe du code tout en améliorant sa structure interne.
On peut identifier trois approches que les managers adoptent généralement face à la refactorisation du code :
- La refactorisation itérative: votre application ne peut pas être développée parfaitement dès la première itération, même si l'équipe compte les meilleurs architectes, concepteurs et développeurs. Une façon pragmatique de refactoriser sans investir beaucoup d'argent ni perdre de temps est d'intégrer la refactorisation dans le processus de développement et de l'effectuer après chaque itération.
- La refactorisation quand c'est nécessaire: après le déploiement de l'application, certains bugs sont signalés ; si leur résolution prend beaucoup de temps, ou si certaines exigences du client sont très difficiles à développer et à intégrer au système existant, la refactorisation peut être une bonne solution pour améliorer la qualité de la base de code. Mais dans ce cas, cela peut être très risqué, et il faut prendre soin d'éviter les régressions dans le code existant.
- Pas de refactorisation: parfois, même s'il y a de nombreux problèmes dans l'application existante, la refactorisation n'est jamais entreprise parce que la direction ne veut pas investir dans le processus, et l'équipe de support doit gérer le stress généré par tous les bugs et les retours.
Si votre équipe refactorise régulièrement ses projets C++ et a choisi d'utiliser certaines nouvelles fonctionnalités C++11/C++14, il est utile de voir comment d'autres projets C++ bien connus ont refactorisé leur code pour adopter les nouvelles normes.
Le navigateur Chrome est un bon projet à examiner : ils refactorisent régulièrement leur base de code, et dès que les nouvelles normes ont été approuvées et prises en charge par les compilateurs, l'équipe de développement de Chrome a choisi d'avancer avec ces nouvelles normes.
Le navigateur Chrome est un projet C++ mature, et il est très intéressant d'explorer comment son code est implémenté et conçu. En effet, Chrome est utilisé par des millions d'utilisateurs, ce qui exige de son équipe de développement qu'elle produise du code efficace.
Si vous décidez de refactoriser votre base de code C++ pour utiliser les fonctionnalités des nouvelles normes, il vaut la peine de jeter un œil à cet intéressant document Google, qui liste les fonctionnalités C++11/C++14 autorisées ainsi que celles qui sont bannies.
La partie la plus pertinente de ce document est la liste des fonctionnalités bannies et les raisons de leur bannissement. Il vaut vraiment la peine de passer en revue cette liste pour voir si elle s'applique aussi à vos projets.
Voici les fonctionnalités C++11/C++14 bannies, tirées du document :
Fonctionnalités C++11 bannies
| Fonctionnalité ou bibliothèque | Extrait | Description | Lien vers la documentation | Notes et fil de discussion |
|---|---|---|---|---|
| Espaces de noms inline | inline namespace foo { ... } |
Permet une meilleure gestion des versions des espaces de noms | Espaces de noms inline | Banni dans le Guide de style Google. Fonctionnement incertain avec les composants. |
long long Type |
long long var = value; |
Un entier d'au moins 64 bits | Types fondamentaux | Utilisez un type de stdint.h si vous avez besoin d'un nombre 64 bits. Fil de discussion |
| Fonctions membres ref-qualifiées | class T {
void f() & {}
void f() && {}
};
t.f(); // first
T().f(); // second
std::move(t).f(); // second |
Permet aux fonctions membres d'une classe de ne se lier à |this| qu'en tant que rvalue ou lvalue. | Fonctions membres qualifiées const, volatile et ref | Banni dans le Guide de style Google. Ne peut être utilisé dans Chromium qu'avec l'approbation explicite de styleguide/c++/OWNERS. Fil de discussion |
| Littéraux définis par l'utilisateur | type var = literal_value_type |
Permet les expressions de littéraux définis par l'utilisateur | Littéraux définis par l'utilisateur | Banni dans le Guide de style Google. |
| Classe de stockage thread_local | thread_local int foo = 1; |
Place les variables dans le stockage local au thread. | Durée de stockage | Certains effets surprenants sur Mac (discussion, fork). Utilisez SequenceLocalStorageSlot pour le support des séquences, et ThreadLocal/ThreadLocalStoragesinon. |
Fonctionnalités C++14 bannies
| Fonctionnalité | Extrait | Description | Lien vers la documentation | Notes et fil de discussion |
|---|---|---|---|---|
| Déduction du type de retour des fonctions | auto f() { return 42; }
decltype(auto) g() { return 42; } |
Permet de déduire automatiquement le type de retour d'une fonction à partir de ses instructions return, selon les règles des templates ou d' decltype. |
Déduction du type de retour | Temporairement banni car cela peut provoquer des boucles infinies dans clang. Nous prévoyons de l'autoriser une fois ce bug corrigé. L'usage devrait être rare, principalement pour le code de templates abstraits. Fil de discussion |
| Lambdas génériques | [](const auto& x) { ... } |
Permet de déduire les types des arguments des lambdas en utilisant auto (selon les règles qui s'appliquent aux templates). |
expressions lambda | Temporairement banni car cela peut provoquer des boucles infinies dans clang. Nous prévoyons de l'autoriser une fois ce bug corrigé. Fil de discussion |
Fonctionnalités de bibliothèque C++11 bannies
| Fonctionnalité | Extrait | Description | Lien vers la documentation | Notes et fil de discussion |
|---|---|---|---|---|
| Stockage aligné | std::aligned_storage<10, 128> |
Stockage non initialisé pour les objets exigeant un alignement spécifique. | std::aligned_storage | L'implémentation de MSVC 2017 n'aligne pas sur des frontières supérieures à sizeof(double) = 8 octets. Utilisez alignas(128) char foo[10]; à la place. Patch où cela a été découvert. |
| Opérations de liaison (bind) | std::bind(function, args, ...) |
Déclare un objet fonction lié à certains arguments | std::bind | Utilisez base::Bind à la place. Comparé à std::bind, base::Bind aide à prévenir les problèmes de durée de vie en empêchant la liaison de lambdas avec captures et en forçant les appelants à déclarer les pointeurs bruts comme Unretained. Fil de discussion |
| Environnement flottant C | <cfenv>, <fenv.h> |
Fournit des drapeaux d'état flottant et des modes de contrôle pour le code compatible C | En-tête de bibliothèque standard <cfenv> | Banni par le Guide de style Google en raison d'inquiétudes sur le support des compilateurs. |
| Utilitaires de date et d'heure | <chrono> |
Une bibliothèque standard de date et d'heure | Utilitaires de date et d'heure | Chevauche les API Time de base/. Continuez à utiliser les classes base/. |
| Exceptions | <exception> |
Améliorations du lancer et de la gestion des exceptions | En-tête de bibliothèque standard <exception> | Les exceptions sont bannies par le Guide de style Google et désactivées dans les compilations de Chromium. Notez que le spécificateur noexcept est explicitement autorisé ci-dessus. Fil de discussion |
| Objets fonction | std::function |
Encapsule une fonction polymorphe standard | std::function | Utilisez base::Callback à la place. Comparé à std::function, base::Callback prend directement en charge les classes de comptage de références et les pointeurs faibles de Chromium et gère des problèmes supplémentaires de sécurité des threads. Fil de discussion |
| Classe template Ratio | std::ratio<numerator, denominator> |
Fournit des nombres rationnels à la compilation | std::ratio | Banni par le Guide de style Google en raison d'inquiétudes sur son lien avec un style d'interface plus lourd en templates. |
| Expressions régulières | <regex> |
Une bibliothèque standard d'expressions régulières | Bibliothèque d'expressions régulières | Chevauche de nombreuses bibliothèques d'expressions régulières dans Chromium. En cas de doute, utilisez re2. |
| Pointeurs partagés | std::shared_ptr |
Permet la possession partagée d'un pointeur via des compteurs de références | std::shared_ptr | Nécessite beaucoup plus d'évaluation pour Chromium, et il n'y a pas assez de pression en faveur de cette fonctionnalité. Guide de style Google. Fil de discussion |
| Bibliothèque de threads | <thread> et les en-têtes associés, dont
<future>, <mutex>, <condition_variable> |
Fournit une bibliothèque standard de multithreading utilisant std::thread et associés |
Bibliothèque de support des threads | Chevauche de nombreuses classes de base/. Continuez à utiliser les classes base/ pour l'instant. base::Thread est étroitement couplé à MessageLoop ce qui le rendrait difficile à remplacer. Nous devrions étudier l'utilisation des mutex standard, ou de unique_lock, etc. pour remplacer nos classes de verrouillage/synchronisation. |
Fonctionnalités de bibliothèque C++14 bannies
Cette section liste les fonctionnalités de bibliothèque C++14 qui ne sont pas autorisées dans la base de code Chromium.
| Fonctionnalité | Extrait | Description | Lien vers la documentation | Notes et fil de discussion |
|---|---|---|---|---|
std::chrono literals |
using namespace std::chrono_literals;
auto timeout = 30s; |
Permet de construire plus facilement les types std::chrono. |
std::literals::chrono_literals::operator""s | Banni parce que <chrono> est banni. |
Conclusion
Explorer le code source de projets open source bien connus pour comprendre leurs choix de conception et d'implémentation est peut-être l'un des meilleurs moyens d'apprendre à écrire du code efficace. Les projets C++ bien connus sont généralement développés par des experts C++ qui s'efforcent d'appliquer les bonnes pratiques.
Pour résumer, jetez un œil au code source de Chromium — il vaut vraiment votre temps.
