Las variables temporales son una de las fuentes más comunes de complejidad innecesaria en el código de software. Se acumulan en métodos extensos, dan nombres vagos a los valores calculados y dificultan la extracción, prueba o reutilización de la lógica que contienen. La refactorización "Reemplazar variable temporal con consulta", catalogada por Martin Fowler en " Refactoring: Improving the Design of Existing Code" , aborda este problema directamente: en lugar de almacenar un valor calculado en una variable local, se extrae el cálculo a un método con nombre, una consulta, y se llama a este método donde se necesite el valor.
El resultado es un código que comunica la intención en lugar de ocultarla. El cálculo ya no está enterrado en una asignación de variable al inicio de un método extenso; ahora tiene un nombre, una ubicación y la posibilidad de ser probado de forma aislada. Este artículo abarca la técnica completa, qué son las variables temporales, cuándo se convierten en un problema, cómo realizar la refactorización paso a paso en Java, Python y TypeScript, cuándo aplicarla y cuándo no, y cómo se relaciona con otras técnicas del catálogo de refactorización.
Refactoriza tu código con confianza
SMART TS XL trazas donde se utilizan variables temporales, cálculos duplicados y métodos de consulta extraídos.
Aprende más¿Qué es una variable temporal (Temp) en programación?
Una variable temporal, comúnmente llamada ` temp` , es una variable local dentro de una función o método que almacena un resultado intermedio para su uso dentro del mismo ámbito. Se calcula una sola vez, se guarda en una variable con nombre y luego se referencia en la misma función. La variable existe solo durante la ejecución de la llamada a la función; no es accesible fuera de ella y no se almacena en el estado del objeto.
pitón
# 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;
mecanografiado
// TypeScript: basePrice is a temp variable
const basePrice = quantity * itemPrice; // temp
if (basePrice > 1000) return basePrice * 0.95;
return basePrice * 0.98;
Las variables temporales no son intrínsecamente malas. Tienen usos legítimos: capturar el resultado de una operación costosa que sería un desperdicio repetir, dividir un cálculo complejo de varios pasos en etapas legibles o almacenar valores que se acumulan a lo largo de las iteraciones de un bucle. El problema surge cuando se usan de forma automática para valores derivados simples que serían más claros como métodos con nombre, o cuando se acumulan a lo largo de un método extenso y obligan a los lectores a rastrear múltiples valores intermedios activos simultáneamente.
¿Qué es la refactorización en la ingeniería de software?
La refactorización es el proceso de reestructurar el código existente sin modificar su comportamiento observable. El objetivo es mejorar la calidad interna del código: su legibilidad, capacidad de prueba, mantenibilidad y modularidad. Una refactorización no añade funcionalidades ni corrige errores; simplemente cambia la estructura del código, conservando su función.
Reemplazar Temp por Query es una de las varias docenas de técnicas de refactorización descritas por Martin Fowler. Pertenece a una familia de técnicas que abordan métodos que se han vuelto demasiado largos o complejos:
| Técnica de refactorización | Que hace |
|---|---|
| Reemplazar Temp con Query | Extrae el cálculo de una variable temporal en un método con nombre. |
| Método de extracción | Extrae un bloque de código en un nuevo método con nombre. |
| Temperatura en línea | Reemplaza una simple temp con su expresión directamente. |
| Dividir variable temporal | Separa una plantilla temporal que se reutiliza para diferentes propósitos en variables distintas. |
| Reemplazar Loop por Pipeline | Reemplaza un bucle imperativo con una canalización funcional (map, filter, reduce). |
| Introducir la variable explicativa | Introduce una variable temporal con nombre para aclarar una expresión compleja. |
Estas técnicas no se utilizan de forma aislada. Fowler describe la sustitución de variables temporales por consultas como un paso fundamental antes de la extracción de métodos: si un método contiene variables temporales, extraer una parte del mismo a un método nuevo se complica porque dichas variables pueden utilizarse tanto antes como después de la sección extraída. Eliminar primero las variables temporales, convirtiéndolas en consultas, facilita la extracción.
¿Qué es reemplazar Temp con Query?
La técnica de refactorización "Reemplazar variable temporal con consulta" transforma una variable temporal local en una llamada a un método. En lugar de calcular un valor y asignarlo a una variable local, se extrae el cálculo a un método privado, la consulta, que devuelve el valor calculado al ser llamado. Dondequiera que se usara la variable temporal, se reemplaza la referencia con una llamada al método de consulta.
El ejemplo canónico de Refactoring de Fowler :
Antes:
Java
double basePrice = _quantity * _itemPrice;
if (basePrice > 1000)
return basePrice * 0.95;
else
return basePrice * 0.98;
Después:
Java
if (basePrice() > 1000)
return basePrice() * 0.95;
else
return basePrice() * 0.98;
private double basePrice() {
return _quantity * _itemPrice;
}
El método de consulta basePrice() Ahora es un cálculo con nombre y autónomo. Se puede llamar desde cualquier otro método de la clase, probar de forma independiente, sobrescribir en las subclases y comprender sin necesidad de leer primero el método que lo llama.
El problema con las variables temporales
Fragmentan la lógica a través de un método
Una variable temporal divide un cálculo en dos partes separadas: la asignación (donde se calcula el valor) y el uso (donde se lee). En un método corto, esta división no supone ningún problema. En un método que ha crecido hasta alcanzar treinta o cincuenta líneas, la asignación y el uso pueden estar separados por muchas líneas de lógica adicional. El lector debe desplazarse hacia arriba para encontrar la asignación, retener su significado en la memoria de trabajo y volver a desplazarse hasta el uso. Cada variable temporal adicional incrementa esta carga cognitiva.
Bloquean el método de extracción
El principal problema práctico de las variables temporales es que bloquean otras refactorizaciones. Consideremos un método con una bifurcación condicional compleja que se beneficiaría de ser extraída a un método independiente. Si la bifurcación utiliza una variable temporal asignada previamente en el método, la extracción requiere pasar la variable temporal como parámetro, convertirla en una variable de instancia o calcular su valor nuevamente dentro del método extraído. Ninguna de estas opciones es elegante. Eliminar primero la variable temporal, reemplazándola por una consulta, elimina este obstáculo por completo.
Invitan a la reutilización y la mutación.
Las variables temporales a veces se reutilizan para diferentes propósitos dentro del mismo método, una práctica que Fowler llama el “enredo de variables temporales”. Una variable llamada temp or result El hecho de que se reasigne varias veces no proporciona información semántica y, de hecho, induce a error a los lectores sobre lo que representa en cada momento. Incluso las variables temporales de un solo propósito pueden acumularse hasta que el alcance de un método se vea saturado de valores intermedios que los lectores deben seguir simultáneamente.
Paso a paso: Cómo aplicar Reemplazar Temp con Query
La transformación consta de cuatro pasos que pueden aplicarse de forma segura en cualquier idioma:
Paso 1: Confirme que la variable temporal se asigna exactamente una vez y que nunca se modifica. Si la variable temporal se reasigna posteriormente en el método, divídala primero utilizando la función Dividir variable temporal.
Paso 2: Extraiga la parte derecha de la asignación en un método privado. Asígnale al método un nombre que describa qué calcula, no cómo lo hace. basePrice() es mejor que calculateQuantityTimesPrice().
Paso 3: Reemplace cada referencia a la variable temporal con una llamada al nuevo método. La mayoría de los IDE pueden hacerlo automáticamente: haga clic con el botón derecho en la variable temporal → Refactorizar → Variable en línea, y luego Extraer método en la expresión en línea.
Paso 4: Elimine la declaración de la variable temporal. Si la extracción se ha completado, la variable temporal ya no debería tener referencias y puede eliminarse.
Java: Ejemplo completo y funcional
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;
}
}
El método getPrice() Ahora se lee como una sola expresión que comunica claramente el cálculo. Cada consulta extraída se puede leer, probar y extender de forma independiente. Tenga en cuenta que discountFactor() llamadas basePrice(), esto es correcto porque basePrice() Es un cálculo puro sin efectos secundarios, por lo que llamarlo dos veces no introduce ningún riesgo.
Python: Reemplazar Temp con una propiedad
En Python, el equivalente natural de un método de consulta es un @property, lo que permite llamar al método sin paréntesis y se lee de forma idéntica a un acceso a un atributo:
pitón
# 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
pitón
# 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
El uso de @property significa self.base_price se lee de forma idéntica a una variable de instancia, lo que hace que el código que realiza la llamada self.base_price * self.discount_factor Completamente natural. Cada propiedad se puede comprobar de forma independiente:
pitón
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
Este nivel de comprobabilidad es imposible con la versión basada en temp: los cálculos internos base_price y discount_factor no son accesibles desde fuera del método.
TypeScript: Métodos de consulta y getters
TypeScript admite tanto consultas basadas en métodos como métodos de obtención de propiedades, coincidiendo con los patrones disponibles en Java y Python respectivamente:
mecanografiado
// 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;
}
}
mecanografiado
// 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;
}
}
Métodos de consulta de nombres adecuados
El nombre del método de consulta es fundamental. Una extracción con un nombre inadecuado es peor que la versión temporal que reemplazó, ya que crea una indirección opaca: quienes la utilizan deben consultar la definición del método para comprender su funcionamiento, lo que anula su propósito.
Los nombres de los métodos de consulta adecuados siguen estos principios:
Indica qué representa, no cómo se calcula. basePrice() comunica el concepto de negocio. getQuantityTimesItemPrice() describe el cálculo, no el concepto. La distinción importa cuando cambia el cálculo, el nombre del concepto basePrice() permanece estable incluso si la fórmula cambia.
Utilice frases nominales para expresar valores. Los métodos de consulta devuelven valores; no son comandos. discountFactor(), totalAmount(), isEligible() Siga la convención de nombrar lo que se devuelve. calculateDiscount(), processAmount() Sigue la convención imperativa de los comandos, lo cual resulta confuso para los métodos que simplemente calculan y devuelven un valor.
Las consultas booleanas deben leerse como preguntas. isHighValue(), hasDiscount(), meetsThreshold() Comunicar que el resultado es un valor booleano y que quien realiza la llamada está haciendo una pregunta de sí o no. bool 変数名 La nomenclatura de variables booleanas (una consulta en los datos de Search Console) refleja precisamente esta preocupación: las variables y los métodos booleanos necesitan nombres que dejen claro su significado en el momento de su uso.
Mantenga la estabilidad de los nombres en todos los métodos relacionados. If basePrice() es utilizado por discountFactor(), la coherencia en la denominación les indica a los lectores que discountFactor depende basePriceLa nomenclatura inconsistente rompe esta documentación implícita.
Cuándo aplicar Reemplazar Temp con Query
Aplique esta refactorización cuando:
- El puesto temporal se asigna exactamente una vez y nunca se vuelve a asignar.
- El cálculo es una expresión pura: lee de campos o parámetros, pero no modifica el estado externo, no llama a servicios de red ni depende del tiempo o la aleatoriedad.
- El cálculo es lo suficientemente complejo como para que nombrarlo facilite la lectura, o lo suficientemente simple como para que la temperatura sea simplemente un estorbo.
- Estás a punto de aplicar el método Extract a un bloque que utiliza la temperatura
El escenario ideal más común es un valor derivado: un precio, un total, un descuento, una cadena formateada o una clasificación condicional. Estos valores se derivan completamente de los campos del objeto, sin efectos secundarios, y pertenecen naturalmente como propiedades del objeto en lugar de como cálculos intermedios dentro de un método.
Cuándo no aplicar Reemplazar Temp con Query
Operaciones que requieren un alto rendimiento. Si el cálculo es costoso (consulta a la base de datos, llamada a la red, bucle O(n²)), llamar al método de consulta dos veces duplica el costo. La variable temporal existe precisamente para evitar esto. En estos casos, se puede dejar la variable temporal o memorizar el método de consulta (almacenar en caché el resultado después de la primera llamada).
pitón
# 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
Operaciones con efectos secundarios. Si la variable temporal almacena el resultado de una operación que solo debería ejecutarse una vez (generar un ID único, registrar información, escribir en un archivo), convertirla en una consulta provocaría que la operación se ejecutara en cada llamada. Esto modifica el comportamiento del programa, no solo su estructura. No aplique esta refactorización a variables temporales con efectos secundarios.
Temperaturas que se acumulan a lo largo de las iteraciones del bucle. Una temperatura que es el acumulador en un for lazo, total += item.price, no es un candidato para Reemplazar Temp con Consulta. No es un valor derivado; es un estado que se acumula a lo largo de las iteraciones. Considere Reemplazar Bucle con Canalización en su lugar si el bucle es el problema.
Técnicas de refactorización relacionadas
La técnica "Reemplazar Temp con Query" pertenece a un conjunto de técnicas que, en conjunto, eliminan la complejidad innecesaria en los métodos. Comprender esta familia de técnicas ayuda a los desarrolladores a elegir la técnica adecuada para el problema en cuestión.
El método Extract es el complemento más común. Reemplazar Temp por Query a menudo posibilita el uso de Extract Method al eliminar variables que, de otro modo, requerirían un engorroso paso de parámetros entre la parte extraída y el resto del método.
Inline Temp es lo contrario de introducir una variable temporal: reemplaza una variable temporal con su expresión directamente en el código. Utilice Inline Temp cuando la variable temporal no aporta claridad y su expresión ya es legible.
La opción Dividir variable temporal se aplica cuando una misma variable temporal se reutiliza para múltiples propósitos en el mismo método. Divídala en variables separadas con nombres que reflejen cada propósito y, a continuación, aplique Reemplazar variable temporal con consulta a cualquiera de las variables temporales de un solo uso resultantes.
Introducir una variable explicativa es la opción opuesta: si una expresión compleja es difícil de leer, introducir una variable temporal con un nombre descriptivo puede mejorar la claridad. Esta técnica y Reemplazar variable temporal con consulta están en conflicto, y el desarrollador debe evaluar qué enfoque beneficia más al código en cuestión.
Reemplazar Loop por Pipeline aborda un patrón común en el que un bucle con un acumulador temporal puede ser reemplazado por una operación de tubería encadenada (map, filter, reduce), que es más declarativo y más fácil de leer.
Cómo SMART TS XL Admite la refactorización a escala
Reemplazar Temp por Query es una refactorización local: transforma una variable en un método. En un código fuente de tamaño considerable, la pregunta más útil no es "¿cómo aplico esta refactorización?", sino "¿en qué parte del código fuente debo aplicarla y qué se verá afectado al hacerlo?".
SMART TS XL Proporciona el análisis estructural entre bases de código que hace que responder a esta pregunta sea sistemático. Identifica dónde se realiza el mismo cálculo como una variable temporal en múltiples ubicaciones, el patrón que Replace Temp with Query está diseñado para consolidar en un único método de consulta con nombre. Rastrea cómo se utiliza un método de consulta refactorizado una vez que se extrae, haciendo visible el alcance de la refactorización antes de que se realice. Y funciona en diferentes lenguajes: para sistemas empresariales donde los programas COBOL, los servicios Java y las canalizaciones de Python operan en los mismos datos, análisis de código estático y análisis de impacto identificar dónde aparece el mismo cálculo lógico en diferentes formas en diferentes lenguajes, la forma más profunda del problema que Replace Temp with Query aborda a nivel de un solo lenguaje.
Para equipos que trabajan en modernización heredada, SMART TS XL, visualización de dependencias Esto permite ver dónde se utilizan los componentes refactorizados antes de modificarlos, lo que garantiza que extraer un cálculo a un método de consulta no afecte a quienes esperaban la estructura original.
Variables temporales y código autodocumentado
La decisión de reemplazar una variable temporal con una consulta es, en última instancia, una decisión sobre lo que el código debe comunicar. Las variables temporales comunican la implementación: este es el cálculo que realicé para obtener este valor. Los métodos de consulta comunican el dominio: esto es lo que significa este valor. En una clase Order, basePrice() Le indica al lector que este concepto existe en el dominio. double x = quantity * itemPrice Explica al lector una operación aritmética.
A medida que el código evoluciona, los conceptos de dominio necesitan entornos estables. Un cálculo integrado en una variable temporal puede cambiar, duplicarse en múltiples métodos o ser malinterpretado por el siguiente desarrollador que lo lea. Un método de consulta con nombre se puede encontrar, probar, documentar y desarrollar de forma intencionada. Esa estabilidad, en todos los lugares donde se necesita el cálculo y para todos los desarrolladores que trabajarán con él, es lo que convierte a Reemplazar Temp por Consulta en algo más que un simple cambio de sintaxis. Es una decisión sobre cómo el código base comunica el problema que resuelve.