Les variables temporaires figurent parmi les sources les plus fréquentes de complexité inutile dans le code logiciel. Elles s'accumulent dans les méthodes longues, donnent des noms vagues aux valeurs calculées et compliquent l'extraction, le test et la réutilisation de la logique qu'elles contiennent. La refactorisation « Remplacer les variables temporaires par des requêtes », décrite par Martin Fowler dans son ouvrage *Refactoring: Improving the Design of Existing Code* , s'attaque directement à ce problème : au lieu de stocker une valeur calculée dans une variable locale, on extrait le calcul dans une méthode nommée, une requête, et on l'appelle partout où la valeur est nécessaire.
Le résultat est un code qui communique clairement l'intention au lieu de la dissimuler. Le calcul n'est plus noyé dans une affectation de variable en début de méthode complexe ; il possède un nom, un emplacement et peut être testé indépendamment. Cet article présente la technique complète, explique ce que sont les variables temporaires, quand elles posent problème, comment effectuer la refactorisation étape par étape en Java, Python et TypeScript, quand l'appliquer et quand ne pas l'appliquer, et comment elle s'articule avec les techniques de refactorisation existantes.
Refactorisez votre code en toute confiance
SMART TS XL traces où des variables temporaires, des calculs dupliqués et des méthodes de requête extraites sont utilisés.
Apprendre encore plusQu'est-ce qu'une variable temporaire (Temp) en programmation ?
Une variable temporaire, souvent appelée ` temp` , est une variable locale à l'intérieur d'une fonction ou d'une méthode qui stocke un résultat intermédiaire utilisable dans cette même portée. Elle est calculée une seule fois, stockée dans une variable nommée, puis référencée ultérieurement dans la même fonction. La variable n'existe que pendant la durée de l'appel de la fonction ; elle n'est pas accessible en dehors de celle-ci et n'est pas stockée dans l'état de l'objet.
python
# Python: base_price is a temp variable
def calculate_total(quantity, item_price):
base_price = quantity * item_price # temp: computed once, used below
if base_price > 1000:
return base_price * 0.95
return base_price * 0.98
Java
// Java: basePrice is a temp variable
double basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) {
return basePrice * 0.95;
}
return basePrice * 0.98;
manuscrit
// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;
Les variables temporaires ne sont pas intrinsèquement mauvaises. Elles ont des usages légitimes : capturer le résultat d’une opération coûteuse qu’il serait inutile de répéter, décomposer un calcul complexe en plusieurs étapes en phases plus lisibles, ou stocker des valeurs qui s’accumulent au fil des itérations d’une boucle. Le problème survient lorsque les variables temporaires sont utilisées systématiquement pour des valeurs dérivées simples qui seraient plus claires sous forme de méthodes nommées, ou lorsqu’elles s’accumulent au cours d’une longue méthode et obligent le lecteur à suivre plusieurs valeurs intermédiaires actives simultanément.
Qu'est-ce que le refactoring en génie logiciel ?
Le refactoring est le processus de restructuration du code existant sans en modifier le comportement observable. Son objectif est d'améliorer la qualité interne du code : sa lisibilité, sa testabilité, sa maintenabilité et sa modularité. Un refactoring n'ajoute pas de fonctionnalités et ne corrige pas les bogues ; il modifie la structure du code tout en préservant son fonctionnement.
Remplacer Temp par Query est une technique de refactorisation parmi plusieurs dizaines décrites par Martin Fowler. Elle appartient à une famille de techniques qui permettent d'optimiser les méthodes devenues trop longues ou trop complexes.
| Technique de refactorisation | Ce qu'il fait |
|---|---|
| Remplacer Temp par une requête | Extrait le calcul d'une variable temporaire dans une méthode nommée |
| Méthode d'extraction | Extrait un bloc de code dans une nouvelle méthode nommée |
| Température en ligne | Remplace directement une simple variable temporaire par son expression. |
| Diviser la variable temporaire | Sépare une variable temporaire réutilisée à des fins différentes en variables distinctes |
| Remplacer Loop par Pipeline | Remplace une boucle impérative par un pipeline fonctionnel (map, filter, reduce) |
| Introduction de la variable explicative | Introduit une variable temporaire nommée pour clarifier une expression complexe |
Ces techniques ne sont pas utilisées isolément. Le remplacement des variables temporaires par des requêtes est décrit par Fowler comme une étape essentielle avant l'extraction de la méthode : si une méthode contient des variables temporaires, l'extraction d'une partie de celle-ci dans une nouvelle méthode devient complexe car ces variables peuvent être utilisées avant et après la section extraite. Supprimer les variables temporaires en les transformant en requêtes facilite l'extraction.
Qu'est-ce que « Remplacer Temp par une requête » ?
Remplacer Temp par Query est une technique de refactorisation qui transforme une variable temporaire locale en un appel de méthode. Au lieu de calculer une valeur et de l'assigner à une variable locale, le calcul est extrait dans une méthode privée, la requête, qui renvoie la valeur calculée lorsqu'elle est appelée. Partout où la variable temporaire était utilisée, la référence est remplacée par un appel à la méthode de requête.
L'exemple canonique tiré de l'ouvrage « Refactoring » de Fowler :
Avant:
Java
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
Après:
Java
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
private double basePrice() {
return _quantity * _itemPrice;
}
La méthode de requête basePrice() Il s'agit désormais d'un calcul nommé et autonome. Il peut être appelé depuis n'importe quelle autre méthode de la classe, testé indépendamment, redéfini dans les sous-classes et compris sans avoir à lire au préalable la méthode appelante.
Le problème des variables temporaires
Ils fragmentent la logique selon une méthode
Une variable temporaire divise un calcul en deux parties distinctes : l’affectation (où la valeur est calculée) et l’utilisation (où elle est lue). Dans une méthode courte, cette division est sans conséquence. Dans une méthode de trente ou cinquante lignes, l’affectation et l’utilisation peuvent être séparées par de nombreuses lignes de code. Le lecteur doit alors remonter dans le code pour trouver l’affectation, mémoriser sa signification, puis revenir à l’utilisation. Chaque variable temporaire supplémentaire alourdit cette charge cognitive.
Ils bloquent la méthode d'extraction
Le principal problème pratique lié aux variables temporaires est qu'elles bloquent d'autres refactorisations. Prenons l'exemple d'une méthode comportant une branche conditionnelle complexe qui gagnerait à être extraite dans une méthode distincte. Si cette branche utilise une variable temporaire initialisée précédemment dans la méthode, l'extraction nécessite soit de passer la variable temporaire en paramètre, soit d'en faire une variable d'instance, soit de recalculer sa valeur dans la méthode extraite. Aucune de ces solutions n'est élégante. Supprimer la variable temporaire au préalable en la remplaçant par une requête permet de lever complètement cet obstacle.
Ils invitent à la réutilisation et à la mutation
Les variables temporaires sont parfois réutilisées à des fins différentes au sein d'une même méthode, une pratique que Fowler appelle « l'enchevêtrement des variables temporaires ». Une variable nommée temp or result Une variable réaffectée à plusieurs reprises ne fournit aucune information sémantique et induit activement les lecteurs en erreur quant à sa signification à un instant donné. Même les variables temporaires à usage unique peuvent s'accumuler jusqu'à encombrer la portée d'une méthode de valeurs intermédiaires que les lecteurs doivent suivre simultanément.
Procédure pas à pas : Comment remplacer les variables temporaires par des variables de requête
La transformation suit quatre étapes qui peuvent être appliquées sans risque dans n'importe quelle langue :
Étape 1 : Vérifiez que la variable temporaire est assignée une seule fois et qu’elle n’est jamais modifiée. Si elle est réassignée ultérieurement dans la méthode, séparez-la d’abord à l’aide de l’outil « Séparer la variable temporaire ».
Étape 2 : Extraire le membre de droite de l’affectation dans une méthode privée. Donnez à la méthode un nom qui décrit ce qu'elle calcule, et non comment. basePrice() est mieux que calculateQuantityTimesPrice().
Étape 3 : Remplacez toutes les références à la variable temporaire par un appel à la nouvelle méthode. La plupart des IDE peuvent le faire automatiquement : cliquez avec le bouton droit sur la variable temporaire → Refactoriser → Variable en ligne, puis Extraire la méthode sur l’expression en ligne.
Étape 4 : Supprimez la déclaration de la variable temporaire. Si l’extraction est terminée, la variable temporaire ne devrait plus contenir aucune référence et peut être supprimée.
Java : Exemple complet et fonctionnel
Java
// Before: Order class with temporary variables
public class Order {
private int quantity;
private double itemPrice;
public double getPrice() {
double basePrice = quantity * itemPrice; // temp 1
double discountFactor; // temp 2
if (basePrice > 1000)
discountFactor = 0.95;
else
discountFactor = 0.98;
return basePrice * discountFactor;
}
}
Java
// After: temps extracted to query methods
public class Order {
private int quantity;
private double itemPrice;
public double getPrice() {
return basePrice() * discountFactor();
}
private double basePrice() {
return quantity * itemPrice;
}
private double discountFactor() {
return basePrice() > 1000 ? 0.95 : 0.98;
}
}
Procédé getPrice() Elle se lit désormais comme une expression unique qui communique clairement le calcul. Chaque requête extraite peut être lue, testée et étendue indépendamment. Notez que discountFactor() en cours basePrice(), c'est correct parce que basePrice() Il s'agit d'un calcul pur sans effets secondaires, donc l'appeler deux fois ne présente aucun risque.
Python : Remplacer Temp par Property
En Python, l'équivalent naturel d'une méthode de requête est une @property, ce qui permet d'appeler la méthode sans parenthèses et se lit de manière identique à un accès à un attribut :
python
# Before: temporary variables in a method
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def get_price(self):
base_price = self.quantity * self.item_price # temp
discount = 0.95 if base_price > 1000 else 0.98 # temp
return base_price * discount
python
# After: temps replaced with properties (query methods in Python)
class Order:
def __init__(self, quantity, item_price):
self.quantity = quantity
self.item_price = item_price
def get_price(self):
return self.base_price * self.discount_factor
@property
def base_price(self):
return self.quantity * self.item_price
@property
def discount_factor(self):
return 0.95 if self.base_price > 1000 else 0.98
L'utilisation de @property veux dire self.base_price se lit de manière identique à une variable d'instance, rendant le code appelant self.base_price * self.discount_factor Entièrement naturel. Chaque propriété est vérifiable indépendamment :
python
def test_base_price():
order = Order(10, 150)
assert order.base_price == 1500
def test_discount_factor_high_value():
order = Order(10, 150) # base_price = 1500 > 1000
assert order.discount_factor == 0.95
def test_get_price():
order = Order(10, 150)
assert order.get_price() == 1500 * 0.95
Ce niveau de testabilité est impossible avec la version basée sur la temp : les calculs internes base_price et discount_factor ne sont pas accessibles depuis l'extérieur de la méthode.
TypeScript : Méthodes de requête et accesseurs
TypeScript prend en charge à la fois les requêtes basées sur des méthodes et les accesseurs de propriétés, correspondant aux modèles disponibles respectivement en Java et en Python :
manuscrit
// Before: temporary variables
class Order {
constructor(private quantity: number, private itemPrice: number) {}
getPrice(): number {
const basePrice = this.quantity * this.itemPrice; // temp
const discount = basePrice > 1000 ? 0.95 : 0.98; // temp
return basePrice * discount;
}
}
manuscrit
// After: TypeScript getters replace temps
class Order {
constructor(private quantity: number, private itemPrice: number) {}
getPrice(): number {
return this.basePrice * this.discountFactor;
}
private get basePrice(): number {
return this.quantity * this.itemPrice;
}
private get discountFactor(): number {
return this.basePrice > 1000 ? 0.95 : 0.98;
}
}
Bien nommer les méthodes de requête
Le nom de la méthode de requête est primordial. Une extraction mal nommée est pire que la méthode temporaire qu'elle remplace, car elle crée une indirection opaque : les appelants doivent consulter la définition de la méthode pour comprendre son fonctionnement, ce qui est contre-productif.
Les noms de méthodes de requête appropriés suivent ces principes :
Nommez ce que cela représente, et non comment cela est calculé. basePrice() communique le concept commercial. getQuantityTimesItemPrice() décrit le calcul, et non le concept. Cette distinction est importante lorsque le calcul change, le nom du concept basePrice() reste stable même si la formule change.
Utilisez des groupes nominaux pour exprimer les valeurs. Les méthodes de requête renvoient des valeurs ; ce ne sont pas des commandes. discountFactor(), totalAmount(), isEligible() Respectez la convention de dénomination des éléments renvoyés. calculateDiscount(), processAmount() suivre la convention impérative des commandes, ce qui est source de confusion pour les méthodes qui se contentent de calculer et de renvoyer.
Les requêtes booléennes doivent être formulées comme des questions. isHighValue(), hasDiscount(), meetsThreshold() Indiquez clairement que la réponse est booléenne et que l'appelant pose une question fermée (oui/non). bool 変数名 (La dénomination des variables booléennes, une requête dans les données de la Search Console) reflète précisément cette préoccupation : les variables et méthodes booléennes ont besoin de noms qui rendent leur signification claire au moment de leur utilisation.
Veillez à conserver des noms cohérents entre les méthodes apparentées. If basePrice() est utilisé par discountFactor(), la cohérence de la dénomination indique aux lecteurs que discountFactor dépend basePriceUne nomenclature incohérente compromet cette documentation implicite.
Quand appliquer le remplacement temporaire par une requête
Appliquez cette refactorisation lorsque :
- La fonction temporaire est attribuée une seule fois et n'est jamais réattribuée.
- Le calcul est une expression pure : il lit des champs ou des paramètres mais ne modifie pas l'état externe, n'appelle pas de services réseau et ne dépend ni du temps ni du hasard.
- Le calcul est soit suffisamment complexe pour que le nommer facilite la lisibilité, soit suffisamment simple pour que la température ne soit qu'un encombrement.
- Vous êtes sur le point d'appliquer la méthode Extraction à un bloc qui utilise le temp
Le scénario idéal le plus courant est celui d'une valeur dérivée : un prix, un total, une remise, une chaîne formatée, une classification conditionnelle. Ces valeurs sont entièrement issues des champs de l'objet, sans aucun effet de bord, et relèvent donc naturellement du statut de propriétés de l'objet plutôt que de calculs intermédiaires au sein d'une méthode.
Quand ne pas appliquer : remplacer Temp par Query
Opérations sensibles aux performances. Si le calcul est coûteux (requête de base de données, appel réseau, boucle O(n²)), appeler la méthode de requête deux fois double le coût. La variable temporaire sert précisément à éviter cela. Dans ces cas, il est possible soit de conserver la variable temporaire, soit de mémoriser la méthode de requête (mettre en cache le résultat après le premier appel).
python
# Memoized property: computed once, cached
from functools import cached_property
class Order:
@cached_property
def expensive_validation(self):
return self.external_service.validate(self.data) # called once, cached
Opérations à effets de bord. Si la variable temporaire contient le résultat d'une opération qui ne devrait s'exécuter qu'une seule fois (génération d'un identifiant unique, journalisation, écriture dans un fichier), la transformer en requête exécuterait l'opération à chaque appel. Cela modifie le comportement du programme, et pas seulement sa structure. N'appliquez pas cette refactorisation aux variables temporaires à effets de bord.
Temps qui s'accumulent au fil des itérations de la boucle. Une température qui est l'accumulateur dans un for boucle, total += item.priceCette valeur ne convient pas à l'option « Remplacer la valeur temporaire par une requête ». Il ne s'agit pas d'une valeur dérivée, mais d'un état qui se construit au fil des itérations. Si la boucle est à l'origine du problème, envisagez plutôt l'option « Remplacer la boucle par un pipeline ».
Techniques de refactoring connexes
L'utilisation de `Temp` par `Query` fait partie d'une famille de techniques qui, collectivement, éliminent la complexité inutile des méthodes. Comprendre cette famille de techniques aide les développeurs à choisir la technique la plus adaptée au problème rencontré.
La méthode Extract est la plus souvent utilisée. Remplacer Temp par Query permet souvent d'utiliser Extract en effaçant les variables qui, autrement, nécessiteraient un passage de paramètres complexe entre la partie extraite et le reste de la méthode.
L'utilisation d'une variable temporaire en ligne est l'inverse de l'introduction d'une variable temporaire : elle remplace directement une variable temporaire par son expression dans le code. Utilisez cette méthode lorsque la variable temporaire n'apporte aucune clarté et que son expression est déjà lisible.
L'option « Fractionner la variable temporaire » s'applique lorsqu'une même variable temporaire est réutilisée à plusieurs reprises dans une même méthode. Fractionnez-la en variables distinctes dont les noms reflètent chaque usage, puis appliquez l'option « Remplacer la variable temporaire par une requête » à chacune de ces variables temporaires à usage unique.
L'introduction d'une variable explicative est l'approche inverse : si une expression complexe est difficile à lire, l'ajout d'une variable temporaire avec un nom descriptif peut améliorer sa lisibilité. Cette technique et le remplacement d'une variable temporaire par une requête sont contradictoires, et le développeur doit déterminer quelle approche est la plus pertinente pour le code concerné.
Remplacer Loop par Pipeline aborde un schéma courant où une boucle avec un accumulateur temporaire peut être remplacée par une opération de pipeline enchaînée (map, filter, reduce), ce qui est plus déclaratif et plus facile à lire.
Comment SMART TS XL Prend en charge la refactorisation à grande échelle
Remplacer Temp par Query est une refactorisation locale : elle transforme une variable dans une méthode. Dans un code source d'une certaine taille, la question pertinente n'est pas « comment appliquer cette refactorisation ? » mais « où dans l'ensemble du code source dois-je l'appliquer, et quelles seront les conséquences ? »
SMART TS XL Cette fonctionnalité fournit l'analyse structurelle transversale du code qui permet de répondre systématiquement à cette question. Elle identifie les occurrences d'un même calcul effectué à plusieurs endroits sous forme de variable temporaire, un modèle que la fonctionnalité « Remplacer les variables temporaires par une requête » vise à consolider en une seule méthode de requête nommée. Elle retrace l'utilisation d'une méthode de requête remaniée une fois extraite, rendant ainsi visible la portée de la remaniation avant même sa mise en œuvre. Enfin, elle fonctionne avec différents langages : pour les systèmes d'entreprise où des programmes COBOL, des services Java et des pipelines Python opèrent sur les mêmes données, analyse de code statique et analyse d’impact identifier où le même calcul logique apparaît sous différentes formes dans différentes langues, la forme plus profonde du problème que Replace Temp with Query aborde au niveau d'une seule langue.
Pour les équipes travaillant sur modernisation de l'héritage, SMART TS XL's visualisation des dépendances permet de voir où les composants remaniés sont utilisés avant de les modifier, garantissant ainsi que l'extraction d'un calcul dans une méthode de requête ne perturbe pas les appelants qui s'attendaient à la structure d'origine.
Variables temporaires et code auto-documenté
Le choix de remplacer une variable temporaire par une requête dépend en fin de compte de ce que le code doit communiquer. Les variables temporaires communiquent l'implémentation : voici le calcul effectué pour obtenir cette valeur. Les méthodes de requête communiquent le domaine : voici la signification de cette valeur. Dans une classe Order, basePrice() indique au lecteur que ce concept existe dans ce domaine. double x = quantity * itemPrice explique au lecteur une opération arithmétique.
À mesure que le code évolue, les concepts du domaine nécessitent un environnement stable. Un calcul exécuté dans une variable temporaire peut être modifié, dupliqué dans plusieurs méthodes, ou mal interprété par le développeur suivant. Une méthode de requête nommée, en revanche, peut être trouvée, testée, documentée et faire l'objet d'évolutions intentionnelles. Cette stabilité, partout où le calcul est nécessaire et pour tous les développeurs qui l'utiliseront, fait de « Remplacer les variables temporaires par des requêtes » bien plus qu'un simple changement de syntaxe. Il s'agit d'un choix concernant la manière dont le code communique le problème qu'il résout.


