Problema
Se necesita crear una lista enlazada para cualquier tipo de dato existente: primitivos, objetos y objetos creados por el usuario.
Solución 1: Una lista por cada tipo de datos
Para este caso decidimos crear una lista enlazada que almacene valores enteros. Esto significa que el Nodo tendrá un dato con tipo int.
class Nodo {
int dato;
Nodo siguiente;
}
Lenguaje del código: JavaScript (javascript)
El problema de esta situación es que se creará una estructura que funciona solamente para datos de tipo entero. Cualquier otro tipo que no pueda convertirse a entero queda descartado (Por ejemplo: boolean, números flotantes, cadenas y casi todos los objetos).
Entonces debe crearse una lista enlazada especifica para cada tipo de dato: una para los String, otra para valores boolean, otra para valores double, etc… Tantas versiones como tipos de datos se necesiten. Eso es mucho trabajo.
Solucion 2: Uso de Object
Object es el tipo de dato del cual se deriva cualquier tipo no primitivo en Java y al utilizar las técnicas del autoboxing y unboxing, es posible convertir cualquier dato en Object y viceversa. Para el caso de los primitivos se utilizan los tipos wrappers.
class NodoObj {
Object dato;
NodoObj siguiente;
}
Lenguaje del código: JavaScript (javascript)
En el caso anterior es posible utilizar una sola base de código para generar una lista enlazada y que sea útil para cada tipo de dato.
public class NodoObj {
Object dato;
NodoObj siguiente;
public NodoObj(Object dato) {
this.dato = dato;
}
public Object getDato() {
return dato;
}
public static void main(String[] args) {
// Almacenando un dato entero
NodoObj n1 = new NodoObj(2);
// Para utilizar ese dato hay que realizar el 'unboxing'
int valor = (int) n1.getDato();
// No hay control del tipo de dato a insertar
NodoObj n2 = new NodoObj(true);
NodoObj n3 = new NodoObj("hola");
NodoObj n4 = new NodoObj(n3);
}
}
Lenguaje del código: JavaScript (javascript)
Pero al revisar el código detenidamente se observa lo siguiente:
- El dato de tipo Object debe convertirse de forma explícita cada vez que se desea utilizar el tipo de dato original (unboxing).
- No hay una revisión de tipo de datos, lo cual permite que podamos establecer dentro del Nodo diferentes tipos de datos a la vez, lo cual puede ser una ventaja en ocasiones pero cuando sean necesarios datos homogéneos esto es una problema.
Solución 3: Genéricos
En este caso se hace uso similar a la solución 2, de una receta general en lugar de fabricar innumerables opciones.
// La clase lleva la notacion de diamante
public class NodoT<T> {
// El tipo de dato generico esta denominado como 'T'
T dato;
// Todas las menciones de la estructura deben
// llevar la notacion de diamante
NodoT<T> siguiente;
}
Lenguaje del código: PHP (php)
Para este caso, hay cierta sintaxis que se agrega al código. Sin embargo, una vez conocida la técnica resulta fácil de utilizar.
// La clase lleva la notacion de diamante
public class NodoT<T> {
// El tipo de dato generico esta denominado como 'T'
T dato;
// Todas las menciones de la estructura deben llevar la
// notacion de diamante
NodoT<T> siguiente;
public NodoT(T dato) {
this.dato = dato;
}
// Es posible retornar el tipo de dato generico. Cuando
// se ejecute tomara el tipo que se establezca en
// el constructor.
public T getDato() {
return dato;
}
public static void main(String[] args) {
// En el constructor se define el tipo de dato a utilizar
NodoT<Integer> n1 = new NodoT<Integer>(2);
// Se puede utilizar el dato sin conversiones
int valor = n1.getDato();
// Hay revision del tipo de dato a utilizar
NodoT<Integer> n1 = new NodoT<Integer>("hola"); // Error
}
}
Lenguaje del código: PHP (php)
Hay otra observación que realizar: No puede utilizarse directamente tipos primitivos en lugar de eso se utilizan las clases wrappers.
Conclusiones
- La regla máxima debe ser DRY. Es decir, evitar repetir código innecesario ya que esto tiende a elevar los errores y el tiempo de mantenimiento de nuestro código.
- En este caso, la solución 1 rompe la regla DRY, haciendo cambios por cada tipo de datos que se necesite incorporar a la solución.
- Otro punto es la incorporación de validaciones y preparaciones extras de los datos. En la solución 2, aunque evitamos el DRY. Es necesario validar que los datos que ingresamos o retornamos son del tipo que corresponden. Esto en una solución automatizada resulta en una desarrollo que puede irse complicando por cada caso.
- Aunque se incorporan ciertos elementos nuevos como la sintaxis de las estructuras genéricas y el hecho de que no se pueden utilizar directamente tipos primitivos, la solución 3 es la mejor opción de las soluciones a largo plazo: evita el DRY y las validaciones a los datos de entrada y salida de nuestra estructura de datos.