març 10, 2021

Àmbit de variables en Java


Reduint l’àmbit

La següent frase has de gravar-la a foc al teu cap des d’aquest mateix moment:

Tota variable ha de ser declarada en l’àmbit més reduït possible.

Quan una variable és declarada amb un àmbit més extens de l’necessari, és només qüestió de temps que usem aquesta variable de forma incorrecta, provocant confusió i invitádonos a introduir error s (si és que l’àmbit incorrecte no és en si mateix ia un error …). Anem a il·lustrar-ho amb un exemple:

public class MiClase {int contarUsuariosPorNombreIncompleto(String fragmentoNombreUsuario, String nombreUsuarios) {boolean encontrado = false;int totalEncontrados = 0;for (Usuario nombreUsuarioActual : nombreUsuarios) {if (nombreUsuarioActual.contains(fragmentoNombreUsuario)) {encontrado = true;}if (encontrado) {totalEncontrados++;}}return totalEncontrados;}public static void main(String args) {String fragmentoNombreUsuario = "Michael";String nombreUsuarios = {"Jhon Doole", "Michael Fletcher", "James 'Jimmy X' Donald"};int resultado = new MiClase().contarUsuariosPorNombreIncompleto(fragmentoNombreUsuario, nombreUsuarios);System.out.println("Total resultados: " + resultado);}}

A l’executar la classe anterior, podem veure per consola la següent sortida:

Total resultados: 2

Què ha passat? Només un dels usuaris té en el seu nom la cadena Michael, però se’ns informa que hi ha dos noms d’usuari coincidents. Depuremos virtualment l’execució de la crida a buscarNumeroUsuarios:

  1. Inici de la primera iteració:
    • trobat és false
    • totalEncontrados és 0
  2. Fi de la primera iteració:
    • trobat és false
    • totalEncontrados és 0
  3. Inici de la segona iteració:
    • trobat és false
    • totalEncontrados és 0
  4. Fi de la segona iteració:
    • trobat és true
    • totalEncontrados es 1
  5. Inici de la tercera i última iteració:
    • trobat és true (però hauria de ser false)
    • totalEncontrados es 1
  6. Fi de la tercera i última iteració:
    • trobat és true
    • totalEncontrados es 2

Sense entrar a valorar que l’ús de dos blocs if per la lògica del nostre problema (buscar un usuari per una fragment del seu nom) és totalment innecessari, és el resultat de la variable encontrado la que determina que augmentem el comptador totalEncontrados. El problema és que la variable encontrado hauria de reiniciar-ho a false en l’inici (o final) de cada iteració, o en cas contrari en el moment en que trobem una coincidència la resta d’iteracions donaran un fals positiu:

public class MiClase {int contarUsuariosPorNombreIncompleto(String fragmentoNombreUsuario, String nombreUsuarios) {boolean encontrado = false;int totalEncontrados = 0;for (Usuario nombreUsuarioActual : nombreUsuarios) {if (nombreUsuarioActual.contains(fragmentoNombreUsuario)) {encontrado = true;}if (encontrado) {totalEncontrados++;}encontrado = false;}return totalEncontrados;}// ...}

Ara a l’executar de nou la nostra classe obtenim el resultat esperat per les dades que hem proporcionat:

Total resultados: 1

Bé, ja hem resolt el nostre odiós error. Ara és el moment de parar-nos i avaluar la situació. Si estàs pensant en deixar el codi tal qual és que: ets inexpert (perdonable), o tens pressa (no perdonable), o simplement no t’importa (punible).

Oblidant quin tipus de programador vols ser, continuarem. ¿No et sembla que estem fent un ús una mica retorçat de la variable encontrado?:

  • La declarem com a variable de bloc (àmbit local). ..
  • … dins el mètode buscarNumeroUsuarios()...
  • … però només l’estem fent servir dins el bloc for (un àmbit local més concret)

Recordes aquella frase que havies de gravar-te a foc al cap?

Tota variable ha de ser declarada en l’àmbit més reduït possible.

la nostra variable està sent declarada en un àmbit superior a aquell en què és usada, i per tant hem de reduir el seu àmbit:

public class MiClase {int contarUsuariosPorNombreIncompleto(String fragmentoNombreUsuario, String nombreUsuarios) {int totalEncontrados = 0;for (Usuario nombreUsuarioActual : nombreUsuarios) {boolean encontrado = false;if (nombreUsuarioActual.contains(fragmentoNombreUsuario)) {encontrado = true;}if (encontrado) {totalEncontrados++;}}return totalEncontrados;}// ...}

Ara que la variable es troba en el seu àmbit adequat, el codi original està lliure d’errors, i hem reduït l’ús de la variable de tres a dos llocs, el que significa codi més net. ¿Podíem reduir encara més l’àmbit de la variable booleana? En aquest cas concret sí, eliminant-la per complet (no hi ha variable, no hi ha àmbit):

public class MiClase {int buscarNumeroUsuarios(String nombreParcialUsuarioBusqueda, String nombreUsuarios) {int totalEncontrados = 0;for (Usuario nombreUsuarioActual : nombreUsuarios) {if (nombreUsuarioActual.contains(nombreParcialUsuarioBusqueda)) {totalEncontrados++;}}return totalEncontrados;}// ...}

Ara la nostra lògica de negoci és bastant més senzilla d’entendre. L’eliminació de la variable encontrado no és tècnicament una reducció d’àmbit, sinó una refactorització (millora de codi).

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *