Friday, September 7, 2012

Builder vs Constructor ("Buildea" un objeto)

Continúo con el post anterior, describiendo la construcción de objetos mediante un Builder.
El patrón de diseño Builder es uno de los patrones descritos en el libro Design Patterns: Elements of Reusable Object-Oriented Software. Es un patrón creacional que separa el proceso de construcción de un objeto complejo de su representación, de manera que el mismo proceso de construcción pueda utilizarse para diferentes representaciones.
Una variante de este patrón puede utilizarse para resolver problemas con la creación de objetos, como es el problema que hemos descrito del telescoping constructor. La idea es que la clase Estudiante se asocie "fuertemente" con un objeto colaborador, que se encargue del proceso de construcción de los objetos de clase Estudiante, y que el cliente haga uso directo de ese Builder para ordenar la creación de un estudiante, una vez que el Builder tenga todos los "ingredientes" necesarios para hacerlo.
Esa asociación fuerte (una relación de amistad en C++) se logra haciendo que la clase Builder quede anidada dentro de la clase Estudiante y que sea utilizada directamente por el cliente. El método de construcción del Builder tendrá que tener acceso a la privacidad de la clase Extudiante para lograr el objetivo.
El código entonces quedaría de la siguiente forma:
public class Estudiante {
  private final int id;   //Podemos regresar al caso inicial con atributos finales
  private final String nombre;
  private final Calendar fechaDeNacimiento;
  private String carrera;
  private int semestre;
  private  String imss;
  private  String issste;
  private boolean gimnasio;
  private boolean becario;
// Viene ahora el Builder para el Estudiante
  public static class EstudianteBuilder {
    //Parámetros requeridos
    private final int id;
    private final String nombre;
    private final Calendar fechaDeNacimiento;
    private String carrera;
    private int semestre;
    
    // Parámetros opcionales
    private String imss =null;
    private String  issste=null;
    private boolean gimnasio=false;
    private boolean becario=false;
//Constructor del Builder
    public EstudianteBuilder(int id, String nombre, Calendar fechaDeNacimiento, String carrera, int semestre) {
       this.id =id;
       this.nombre = nombre;
       this.fechaDeNacimiento = fechaDeNacimiento;
       this carrera = carrera;
       this.semestre = semestre;
    }
    // Simulando parámetros con nombre
    public EstudianteBuilder imss( String imss) { this.imss = imss; return this; }
    public EstudianteBuilder issste( String issste) {this.issste = issste; return this;}
    public EstudianteBuilder gimnasio(boolean gimnasio) { this.gimnasio= gimnasio; return this;}
    public EstudianteBuilder becario( boolean becario) { this.becario = becario; return this;}
    //El build del Builder
    public Estudiante build(} {
       return new Estudiante(this);
    }
  } // Termina la clase EstudianteBuilder y continúa la clase Estudiante.
  private Estudiante (EstudianteBuilder builder) {
     id = builder.id;
     nombre = builder.nombre;
     fechaDeNacimiento = builder.fechaDeNacimiento;
     carrera = builder.carrera;
     semestre = builder.semestre;
      imss = builder.imss;
      issste = builder.issste;
     gimnasio = builder.gimnasio;
     becario = builder.becario;
  }
} // Fin de la clase Estudiante
La creación de objetos de clase Estudiante se realizará como sigue:
Estudiante.EstudianteBuilder builder = new EstudianteBuilder(12344, "Louis", new Calendar.Builder().setDate(1760,01,01).build(), "Vampirismo", 8);
// Tiene seguro social y es becario. No va al gimnasio ni tiene issste.
Estudiante louis = builder.imss("435678").becario(true).build();
¿Parámetros con nombre? Ahí lo dejo.
Nota al margen: Lo de Buildea es a propósito para generar ira.

Thursday, September 6, 2012

Siguiendo el estilo Java Beans


En el post anterior ( http://ferrocastro.blogspot.mx/2012/09/como-crear-objetos.html) nos referimos al uso de métodos estáticos de fábrica para complementar la creación de objetos por esa vía, en lugar de usar los constructores de la clase. Veamos ahora otros problemas que surgen con la creación de objetos con constructores y cómo pueden solucionarse.
Supongamos que en un sistema de control escolar, modelamos el concepto estudiante con la clase siguiente:
public class Estudiante {
  private final  int id; // El número de matrícula del estudiante
  private final String nombre; // Sus nombres y apellidos
  private final Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  private String imss; // El número de seguridad social del IMSS
  private String issste; // el número del seguro social del ISSSTE
  private boolean gimnasio;// el estudiante hace uso o no del gimnasio
  boolean becario; // el estudiante realiza actividades de becario
  // Constructores para la clase Estudiante
  // Vienen aquí los constructores de la clase
  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Se nos pueden ocurrir otros atributos, o que algunos de los atributos que hemos añadido pueden encapsularse en otras clases, pero el objetivo no es discutir sobre el buen diseño de los atributos. Lo que queremos destacar, es que de ese conjunto de atributos, algunos será siempre requeridos y otros serán opcionales, y que esa consideración debe tomarse en cuenta para el diseño de los constructores de la clase.
Supongamos (pienso que es una buena suposición) que los valores de los atributos id, nombre, fechaDeNacimiento, carrera y semestre siempre se requieren para crear un objeto de clase Estudiante y que el resto de los atributos pueden tomar valores opcionales. Noten que también he establecido que los atributos id, nombre y fechaDeNacimiento son constantes, por lo que estaremos obligados a inicialziarlos, bien en el momento de su declaración (que no hemos hecho) o en sus constructores que tenemos que hacer.
Es costumbre en un caso como éste, escribir varios constructores para aprovechar esos valores opcionales para algunos de los atributos y no tener que pasarlos como argumentos en el caso que esos valores sean los que se desean para el objeto creado.
Procedemos entonces a armar un telescopio "plegable" y escribimos los siguientes constructores para esa clase:
public class Estudiante {
  //Atributos requeridos
  private final  int id; // El número de matrícula del estudiante
  private final String nombre; // Sus nombres y apellidos
  private final Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  //Atributos opcionales
  private String imss =null; // El número de seguridad social del IMSS
  private  String issste=null; // el número del seguro social del ISSSTE
  private boolean gimnasio=false;// el estudiante hace uso o no del gimnasio
  boolean becario=false; // el estudiante realiza actividades de becario
  // Constructores para la clase Estudiante
  public Estudiante( int id, String nombre, Calendar fechadeNacimiento, String carrera, int semestre) {
     this (id, nombre, fechaDeNacimiento, carrera, semestre, null);
  }
 public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss) {
    this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, null);
  }
  public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss,String issste) {
    this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, issste, false);
  }
  public Estudiante(int id,String nombre, Calendar fechaDeNacimiento, String carrera, int semestre, String imss,String issste, boolean gimnasio) {
     this (id, nombre, fechaDeNacimiento, carrera, semestre, imss, issste, gimnasio, false);
  }
  public Estudiante( int id, String nombre, Calendar fechadeNacimiento, String carrera, int semestre, String imss,String issste, boolean gimnasio,  boolean becario) {
    this.id = id;
    this.nombre =nombre;
    this.fechaDeNacimiento = fechaDeNacimiento;
    this.carrera = carrera;
    this.semestre =semestre;
    this.imss =imss;
    this.issste = issste;
    this.gimnasio = gimnasio;
    this.becario = becario;
  }

  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Llegamos entonces a lo que Joshua Bloh le llama el "telescoping ocnstructor", en ausencia de la técnica de parámetros con nombres. Podrían haberse escrito más constructores para considerar todas las posibles combinaciones de los parámetros opcionales, en lugar de tener que especificar valores opcionales en las llamadas a los constructores, pero con los que ya hemos escrito basta para indicar que seguramente habrán otras soluciones "más elegantes".
Salta a la vista, sobre todo para aquellos que ya conocen la técnica de contenedores que administran objetos y que usan inyección de dependencias, que una solución sería escribir setters para los atributos al estilo de Java Beans. El código siguiente realiza eso aunque no escribimos todos los setters:
public class Estudiante {
  private  int id; // El número de matrícula del estudiante
  private String nombre; // Sus nombres y apellidos
  private Calendar fechaDeNacimiento;
  private String carrera; // la carrera que cursa
  private int semestre; //el semestre que cursa
  private String imss =null;; // El número de seguridad social del IMSS
  private String issste =null; // el número del seguro social del ISSSTE
  private boolean gimnasio = false;// el estudiante hace uso o no del gimnasio
  boolean becario= false; // el estudiante realiza actividades de becario
// Constructor sin argumentos para seguir la convención Java Beans
  public Estudiante () {
  }
//Setters:
  public void setId (int id) { this.id =id;}
  public void setNombre(String nombre) {this.nombre =nombre;}
  public void setFechaDeNacimiento( Calendar fechaDeNacimiento) { this.fechaDeNacimiento = fechaDeNacimiento,}
  public void setCarrera (String carrera) {this.carrera =carrera;}
  public void setSemestre (String semestre) {this.semestre = semestre};
  public setImss(String imss) {this.imss = imss;}
  public setIssste(String issste) {this.issste = issste;}
  public setGimnasio( boolean gimnasio) = {this.gimnasio = gimnasio;}
  public setBecario (boolean becario) {this.becario = becario;}
  // Y así con los setters para el resto de los atributos
  // Comportamiento asociado al estudiante
  //Vienen aquí los métodos
}
Con el diseño anterior, creamos un estudiante de la siguiente forma:
Estudiante geek = new Estudiante();
geek.setId(14327);
geek.setNombre("Juan de los Palotes");
geek.setFechaDeNacimento(Calendar.set(1990,12,31);
geek.carrera("Brujeria avanzada");
geek.semestre(9);
geek.becario(true);
Dos problemas se presentan al seguir la convención de Java Beans: no podemos lograr atributos inmutables (noten que suprimí el final) y el proceso de obtener un objeto de tipo Estudiante "consistente" ocupa más de una sentencia, por lo que entre sentencias tenemos un objeto inconsistente. Cuando trabajamos con un modelo de componentes orientado a contenedores, el contenedor no me "entrega" el objeto hasta que lo tenga totalmente consistente y el grado de consistencia lo establecemos nosotros.
¿Habrá(n) otra(s solución(es)?
Por supuesto, pero la dejamos para el siguiente post.

Sunday, September 2, 2012

¿Cómo crear objetos?


La pregunta que da título a esta entrada, parece tener una respuesta trivial, y en efecto la tiene. No existe otra manera de crear objetos en Java u otros lenguajes similares que no sea a través del operador new del lenguaje. Pero la pregunta se hace interesante si nos detenemos a pensar en qué lugar del código debemos escribir esa creación de objetos, de suerte que un usuario de nuestras clases pueda usar objetos de ella.
En estos días estoy platicando sobre la creación de objetos en uno de los cursos que imparto y me resultó interesante compartir esas pláticas aquí. Siempre habrá alguien que se interese por estas cosas.
Java y otros lenguajes de OOP definen "constructores" para la creación de objetos, haciendo uso del mencionado operador y adoptando una sintaxis para ese fin. Todos sabemos que el constructor es una función ( que se note que no digo método) que lleva el mismo nombre de la clase, que no indica su tipo de retorno ni tan siquiera void (aunque por supuesto "retorna" un objeto creado. El Constructor, como cualquier otra función o método tiene en su firma la lista de parámetros de cualquier tipo, que normalmente recibe aorgumentos relacionados con el estado inicial que queremos para el objeto.
Java permite cosas como lo siguiente:
class Foo {
  int foo;
  int bar;
  Foo (int foo, int bar) {
    this.foo = foo;
    this.bar = bar;
  }
  void Foo(int foo,int bar) {
    this.foo = foo;
    this.bar = bar ;
  }
}
que imagino que nadie escribe (llamar a un método con el nombre de la clase), a no ser que sea para auto confundirse, confundir a los demás (peor aun) o para explicar que es algo que permite el lenguaje como es en este caso.
Java permite sobrecargar el constructor, de manera que podamos tener diferentes versiones para crear los objetos. Algunos lenguajes actuales incluyen la técnica de parámetros con nombres (named parameters) y se dan el lujo de no permitir la sobrecarga de constructores, pero eso es otra historia.
Cuando sobrecargamos a los constructores perseguimos el propósito de indicar a los usuarios de nuestra clase "diferentes" variantes de los objetos que ofrecemos. Por ejemplo, quien escribió la clase BigInteger en el core de Java decidió escribir el siguiente constructor para la clase BigInteger:
BigInteger(int bitLength, Random rnd)
para crear un positivo BigInteger "probablemente primo". Esa aseveración se expresa en la documentación o descripción del API de esa clase.
Dicho constructor establece que probablemente generará un número primo. ¿Cómo saber eso? Pues no hay otra forma que leyendo su documentación. ¿Podría lograrse algo más claro en la sintaxis de ese constructor para indicar que es muy probable que el BigInteger generado sea primo?.
Hay varias respuestas a esa pregunta. Una que quizá se le ocurra a alguien es crear una clase ProbablePrime que extienda a BigInteger y que el constructor de esa clase cree un objeto de clase BigInteger con el constructor anterior. Pero hay otra forma más sencilla y es a la que me quiero referir aquí y que muchos también conocen y es la de darle a la clase BigInteger un método estático de fábrica y bautizar a ese método con un nombre más nemotécnico.
¿Qué tal si esa clase ofreciera un método estático como el siguiente?

static BigInteger probablePrime(int bitLength, Random rnd)
¿Cuál línea del código siguiente sería más "limpia" si lo que quiero es generar un número primo?
BigInteger big1 = new BigInteger(10new Random());
BigInteger big2 = BigInteger.probablePrime(10new Random());
Por supuesto que la segunda línea expresa con claridad la intención del programador y Joshua Bloh en su libro "Effective Java" incluye ese precepto en uno de sus items: "Consider static factory methods instead of constructorswhen creating objects"
Esa NO es la única ventaja del uso de métodos estáticos de fábrica. Hay otras que el autor describe, como por supuesto hay algunas desventajas.
Al usar métodos estáticos de fábrica en lugar de constructores, no estamos obligados a crear un objeto brand new cada vez que invocamos el método. Es una ventaja que aprovechan las clases inmutables que permiten el uso de objetos compartidos. Por ejemplo, las clases wrappers de tipos primitivos usan esa técnica para compartir sus objetos inmutables. Sigue el código de uno de los métodos estáticos de fábrica de la clasejava.lang.Integer para encapsular un tipo primitivo.
static Integer valueOf(int i) {
  assert IntegerCache.high >=127;
  if( i >= IntegerCache.low && i <= IntegerCache.high)
    return IntegerCache.cache[ i + ( -IntegerCache.low) ];
  return new Integer(i);
}
Otra ventaja importante del uso de los métodos estáticos de fábrica es que facilitan el diseño de marcos de trabajo basados en interfaces al permitir que un método estático de fábrica retorne un objeto de clase derivada, permitiendo entonces que el marco de trabajo no tenga que hacer publica las implementaciones de sus interfaces y poder cambiar a voluntad esas implementaciones sin afectar el código del cliente. Claro que para lograr ésto, la clase, además de métodos estáticos de fábrica, tiene que incluir constructores públicos o protegidos para permitir que de ella se extienda. El uso entonces de los constructores o de los métodos estáticos de fábrica depende de la pericia y experiencia del programador.
El uso de métodos estáticos de fábrica también se aprovecha para diseñar clases no instanciables por el cliente (clases con constructores privados) que devuelven implementaciones propias del marco de trabajo. Es el caso por ejemplo de la clase java.util.Collectionsque incluye métodos estáticos de fábrica para obtener colecciones. Por ejemplo, el método estático de fábrica siguiente:
public static<T>  Set<T> singleton(T o)
{
  return new SingletonSet<>(o);
}
La clase SingletonSet "no es conocida por el cliente". Es una clase anidada privada de la clase java.util.Collections.
Cuando se usan métodos estáticos de fábrica, es necesario una buena selección de sus nombres. Una de las desventajas de esta técnica es que precisamente las firmas de los métodos estáticos de fábrica no se distinguen de otros métodos estáticos, algo que no sucede con los constructores. El bautizo de métodos es relevante. Robert Martin le dedica un capítulo a ese tópico en su libor "Celan Code"
Otras cosas interesantes con respecto a la creación de objetos
Hay otras cosas interesantes para resolver de manera más elegante algunos pequeños problemas cuando tenemos la necesidad de escribir una clase que requiere de muchos parámetros en su constructor y algunos de esos parámetros siempre se requieren y otros pueden tener valores opcionales. Al resolverlo por la vía "normal" se presenta el efecto "telescopio" en los constructores y existen algunas técnicas para escribir con mayor limpieza, pero ya este post se alargó demasiado y lo dejo para el siguiente.