domingo, 28 de mayo de 2017

Un resumen de Scala for the Impatient, parte 19


Paquetes

Los paquetes en Scala son similares a lo propuesto en java o en los espacios de nombre de C++, esto es un mecanismo para el manejo de nombres, por ejemplo el nombre Map puede referirse a una estructura de datos mapa o diccionario de datos del paquete scala.collection.immutable y scala.collection.mutable sin que haya conflicto. Uno puede acceder a esta clase por medio de su nombre, scala.collection.immutable.Map o scala.collection.mutable.Map. Alternativamente podemos utilizar alias.

Para agregar algo a un paquete  

package com {
package horstmann {
package impatient {
class Employee

}
}
}

Ahora la clase Employee es accesible desde el paquete com.horstmann.impatient.

A diferencia de las clases u objetos, los paquetes pueden ser definidos en multiples archivos. Por ejemplo si decidimos agregar la clase Manager al paquete anterior:

package com {
package horstmann {
package impatient {
class Manager

}
}
}
 
Notemos que no existe una relación entre el directorio que contiene a la clase y el sistema de paquetes.

package com {
package horstmann {
package impatient {
class Employee

}
}
}

package com {
package bigjava {
class Counter

}
}
}

Reglas de Alcance

En Scala, las reglas de ámbito para los paquetes son más consistentes que en Java. Los paquetes de Scala anidan como todos los demás ámbitos, es decir se puede acceder a los nombres del ámbito de que están incluidos en ese ambito. Por ejemplo,

package com {
package horstmann {
object Utils {
def percentOf(value: Double, rate: Double) = value * rate / 100
...
}
package impatient {
class Employee {
...
def giveRaise(rate: scala.Double) {
salary += Utils.percentOf(salary, rate)
}
}
}
}
}

Como se puede ver Utils puede ser accedido por Employee porque esta en el ambito.

package com {
package horstmann {
package impatient {
class Manager {
val subordinates = new collection.mutable.ArrayBuffer[Employee]
...
}
}
}
}

Este código aprovecha el hecho de que el paquete scala siempre se importa. Por lo tanto, el paquete es en realidad scala.collection.

Y ahora supongamos que alguien crea el siguiente paquete, tal vez en un archivo diferente:

package com {
package horstmann {
package collection {
...
}
}
}

Ahora la clase Manager ya no compila. Dado que busca un miembro mutable dentro del paquete com.horstmann.collection y no lo encuentra.

En Java, este problema no puede ocurrir porque los nombres de los paquetes son siempre absolutos, comenzando en la raíz de la jerarquía del paquete. Pero en Scala, los nombres de los paquetes son relativos, al igual que los nombres de las clases internas. Con las clases internas, uno no suele tener problemas porque todo el código está en un archivo, bajo el control de quien está a cargo de ese archivo. Pero los paquetes son estructuras abiertas. Cualquier persona puede contribuir a un paquete en cualquier momento.

Una solución es utilizar nombres de paquetes absolutos, empezando por _root_, por ejemplo:

val subordinates = new _root_.scala.collection.mutable.ArrayBuffer[Employee]

Otro enfoque es usar cláusulas de paquete "encadenadas".