He estado leyendo Refactorización de Martin Fowler . En general, es excelente, pero una de las recomendaciones de Fowler parece estar causando algunos problemas.
Fowler recomienda que reemplace las variables temporales con una consulta, así que en lugar de esto:
double getPrice() {
final int basePrice = _quantity * _itemPrice;
final double discountFactor;
if (basePrice > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice * discountFactor;
}
sacas un método auxiliar:
double basePrice() {
return _quantity * _itemPrice;
}
double getPrice() {
final double discountFactor;
if (basePrice() > 1000) discountFactor = 0.95;
else discountFactor = 0.98;
return basePrice() * discountFactor;
}
En general, estoy de acuerdo, excepto que una razón por la que uso variables temporales es cuando una línea es demasiado larga. Por ejemplo:
$host = 'https://api.twilio.com';
$uri = "$host/2010-04-01/Accounts/$accountSid/Usage/Records/AllTime";
$response = Api::makeRequest($uri);
Si intentara incluir eso, la línea tendría más de 80 caracteres.
Alternativamente, termino con cadenas de código, que en sí mismas no son mucho más fáciles de leer:
$params = MustacheOptions::build(self::flattenParams($bagcheck->getParams()));
¿Cuáles son algunas estrategias para conciliar los dos?
coding-style
refactoring
variables
Kevin Burke
fuente
fuente
$host
y el$uri
ejemplo es una especie de urdido, sin embargo - a menos que el anfitrión se están leyendo desde un entorno u otra entrada, preferiría que estar en la misma línea, incluso si lo hace envoltura o ir fuera del borde.Respuestas:
Cómo
1. Existen restricciones de longitud de línea para que pueda ver + comprender más código. Aún son válidos.
2. Enfatice el juicio sobre la convención ciega .
3. Evite las variables temporales a menos que optimice el rendimiento .
4. Evite el uso de sangría profunda para la alineación en declaraciones de varias líneas.
5. Divida las declaraciones largas en varias líneas a lo largo de los límites de la idea :
Razonamiento
La fuente principal de mis problemas (depuración) con variables temporales es que tienden a ser mutables. Es decir, supondré que son un valor cuando escribo el código, pero si la función es compleja, algún otro fragmento de código cambia su estado a la mitad. (O al contrario, donde el estado de la variable permanece igual pero el resultado de la consulta ha cambiado).
Considere atenerse a las consultas a menos que esté optimizando el rendimiento . Esto mantiene cualquier lógica que haya utilizado para calcular ese valor en un solo lugar.
Los ejemplos que ha dado (¿Java y ... PHP?) Permiten enunciados de varias líneas. Si las líneas se alargan, sepárelas. La fuente jquery lleva esto a los extremos. (¡La primera declaración corre a la línea 69!) No es que esté necesariamente de acuerdo, pero hay otras maneras de hacer que su código sea legible que usar temp vars.
Algunos ejemplos
1. Guía de estilo PEP 8 para python (no es el ejemplo más bonito)
2. Paul M Jones en la Guía de estilo Pear (argumento del medio del camino)
3. Longitud de línea de Oracle + convenciones de envoltura (estratagemas útiles para mantener 80 caracteres)
4. MDN Java Practices (enfatiza el juicio del programador sobre la convención)
fuente
Creo que el mejor argumento para usar métodos auxiliares en lugar de variables temporales es la legibilidad humana. Si usted, como humano, tiene más problemas para leer la cadena de métodos auxiliares que el varialbe temporal, no veo ninguna razón por la que deba extraerlos.
(Por favor corrígeme si estoy equivocado)
fuente
No creo que deba seguir estrictamente las pautas de 80 caracteres o que se deba extraer alguna variable temporal local. Pero las largas colas y las temperaturas locales deben investigarse para encontrar mejores formas de expresar la misma idea. Básicamente, indican que una función o línea dada es demasiado complicada y necesitamos desglosarla. Pero debemos ser cuidadosos, porque romper una tarea en partes malas solo complica la situación. Así que debo dividir las cosas en componentes valiosos y simples.
Déjame ver los ejemplos que publicaste.
Mi observación es que todas las llamadas twilio api comenzarán con "https://api.twilio.com/2010-04-1/", y por lo tanto hay una función reutilizable muy obvia:
De hecho, creo que la única razón para generar una URL es hacer la solicitud, por lo que haría:
De hecho, muchas de las URL realmente comienzan con "Cuentas / $ accountSid", por lo que probablemente también extraería eso:
Y si hacemos de la twilio api un objeto que contenga el número de cuenta, podríamos hacer algo como:
El uso de un objeto $ twilio tiene la ventaja de facilitar las pruebas unitarias. Puedo darle al objeto un objeto $ twilio diferente que en realidad no llama a twilio, que será más rápido y no hará cosas extrañas a twilio.
Miremos al otro
Aquí pensaría en cualquiera de los dos:
o
o
Dependiendo de cuál es el idioma más reutilizable.
fuente
En realidad, no estoy de acuerdo con el eminente Sr. Fowler sobre esto en el caso general.
La ventaja de extraer un método del código anteriormente incorporado es la reutilización del código; el código en el método ahora está separado de su uso inicial, y ahora puede usarse en otros lugares del código sin ser copiado y pegado (lo que requeriría hacer cambios en varios lugares si la lógica general del código copiado alguna vez tuviera que cambiar) .
Sin embargo, de igual valor conceptual, a menudo mayor es la "reutilización del valor". El Sr. Fowler llama a estos métodos extraídos para reemplazar las variables temporales "consultas". Bueno, lo que es más eficiente; consultar una base de datos cada una de las múltiples veces que necesita un valor particular, o consultar una vez y almacenar el resultado (suponiendo que el valor sea lo suficientemente estático como para que no espere que cambie).
Para casi cualquier cálculo más allá del relativamente trivial en su ejemplo, en la mayoría de los idiomas es más barato almacenar el resultado de un cálculo que seguir calculándolo. Por lo tanto, la recomendación general de recalcular a pedido es falsa; Cuesta más tiempo de desarrollador y más tiempo de CPU, y ahorra una cantidad trivial de memoria, que en la mayoría de los sistemas modernos es el recurso más barato de esos tres.
Ahora, el método auxiliar, junto con otro código, podría hacerse "vago". Sería, cuando se ejecuta por primera vez, inicializar una variable. Todas las llamadas posteriores devolverán esa variable hasta que se indique explícitamente al método que vuelva a calcular. Esto podría ser un parámetro para el método, o una marca establecida por otro código que cambia cualquier valor que el cálculo de este método depende de:
Ahora, para este cálculo trivial, es incluso más trabajo realizado que guardado, por lo que generalmente recomiendo seguir con la variable temporal; sin embargo, para cálculos más complejos que generalmente desearía evitar ejecutar varias veces, y que necesita en varios lugares del código, esta es la forma en que lo haría.
fuente
Los métodos auxiliares tienen un lugar, pero debe tener cuidado al garantizar la coherencia de los datos y el aumento innecesario del alcance de las variables.
Por ejemplo, su propio ejemplo cita:
Es evidente que tanto
_quantity
y_itemPrice
son variables globales (o por lo menos a nivel de clase) y por lo tanto existe un potencial para que sean fuera modificada degetPrice()
¡Por lo tanto, existe la posibilidad de que la primera llamada
basePrice()
devuelva un valor diferente de la segunda llamada!Por lo tanto, sugeriría que las funciones auxiliares pueden ser útiles para aislar las matemáticas complejas, pero como reemplazo de las variables locales, debe tener cuidado.
También debe evitar la reducción ad absurdum : ¿debería el cálculo de
discountFactor
derivarse a un método? Entonces su ejemplo se convierte en:Particionar más allá de cierto nivel en realidad hace que el código sea menos legible.
fuente
Si trabaja en un lenguaje con parámetros con nombre (ObjectiveC, Python, Ruby, etc.), las variables temporales son menos útiles.
Sin embargo, en su ejemplo de basePrice, la consulta puede tardar un tiempo en ejecutarse y es posible que desee almacenar el resultado en una variable temporal para su uso futuro.
Sin embargo, al igual que usted, uso variables temporales para consideraciones de claridad y longitud de línea.
También he visto a programadores hacer lo siguiente en PHP. Es interesante y genial para la depuración, pero es un poco extraño.
fuente
La razón detrás de esta recomendación es que desea poder utilizar la misma precomputación en otra parte de su aplicación. Consulte Reemplazar temp con consulta en el catálogo de patrones de refactorización:
Por lo tanto, en su ejemplo de host y URI, solo aplicaría esta recomendación si planeo reutilizar la misma definición de URI o host.
Si este es el caso, debido al espacio de nombres, no definiré un método global uri () o host (), sino un nombre con más información, como twilio_host () o archive_request_uri ().
Luego, para el problema de longitud de línea, veo varias opciones:
uri = archive_request_uri()
.Justificación: en el método actual, desea que el URI sea el mencionado. La definición de URI todavía está factorizada.
uri() { return archive_request_uri() }
Si usa a menudo la recomendación de Fowler, sabría que el método uri () es el mismo patrón.
Si debido a la elección del idioma necesita acceder al método local con un 'self', recomendaría la primera solución, para una mayor expresividad (en Python, definiría la función uri dentro del método actual).
fuente