Skip to content
July 13, 2010 / damianmigo

Patrones de diseño: "The Builder Pattern"

A veces, cuando estamos desarrollando una aplicación, inevitablemente terminamos con clases que tienen muchas propiedades o atributos, haciendo muy díficil la lectura del código al momento de invocar los contructores. Por ejemplo, supongamos que estamos desarrollando una aplicación para clasificar archivos MP3 y que vamos a usar la información contenida en la etiqueta ID3 de la canción, para ello escribimos una clase que contiene los campos de dicha etiqueta:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public ID3(String title, String artist) {
    this.title = title;
    this.artist = artist;
  }

  public ID3(String header, String title, String artist) {
    this.header = header;
    this.title = title;
    this.artist = artist;
  }

  // ... Todos los demás posibles constructores
  
  public ID3(String header, String title, String artist, String album,
      int year, String comment, boolean zeroByte, int track, int genre) {
    this.header = header;
    this.title = title;
    this.artist = artist;
    this.album = album;
    this.year = year;
    this.comment = comment;
    this.zeroByte = zeroByte;
    this.track = track;
    this.genre = genre;
  }
  
  // ... Getters
    
}

La clase anterior la podriamos instanciar de la siguiente manera:

ID3 id3 = new ID3("TAG", "Billie Jean", "Michael Jackson", "Thriller", 2009, "", false, 6, 31);

Para los que somos fanáticos de Michael Jackson facilmente podriamos saber qué el segundo parámetro corresponde al título de la canción y el tercero al nombre del artista, sin embargo para saber que significan los últimos tres valores tendríamos que consultar la documentación de la clase.

Existes dos posibles enfoques para mejorar la legibilidad del código anterior:

1.- Usar el "JavaBeans Pattern"

ó

2.- Usar el "Builder Pattern"

Si usáramos el primer enfoque la clase ID3 se vería así:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public ID3(String title, String artist) {
    this.title = title;
    this.artist = artist;
  }

  public void setHeader(String header) {
    this.header = header;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public void setArtist(String artist) {
    this.artist = artist;
  }

  public void setAlbum(String album) {
    this.album = album;
  }

  public void setYear(int year) {
    this.year = year;
  }

  public void setComment(String comment) {
    this.comment = comment;
  }

  public void setZeroByte(boolean zeroByte) {
    this.zeroByte = zeroByte;
  }

  public void setTrack(int track) {
    this.track = track;
  }

  public void setGenre(int genre) {
    this.genre = genre;
  }
  
  // ... Getters
    
}

Y el código para instanciarla sería el siguiente:

ID3 id3 = new ID3("Billie Jean", "Michael Jackson");

id3.setHeader("TAG");

id3.setAlbum("Thriller");

id3.setYear(2009);

id3.setComment("");

id3.setZeroByte(false);

id3.setTrack(6);

id3.setGenre(31);

El código queda más legible y con suerte si somos fanáticos de Michael Jackson nos evitaremos cualquier consulta a la documentación, pues sabremos que el primer parámetro del constructor es el título de la canción y el segundo el nombre del artista.

Sin embargo este enfoque tiene un pequeño problema, la clase queda mutable y puede ser que en algún momento se vuelva inconsistente. Por ejemplo, supongamos que nuestra aplicación no soporta la edición de las etiquetas ID3 y que alguien invoca el "setter" para la pista. Esta última acción dejaría a nuestro objeto en un estado inconsiste, pues el archivo seguiría teniendo el número de pista original y no el que se estableció.

El segundo enfoque para construir clases con muchos atributos se llama "Builder Pattern" y tiene las dos característacas que buscamos, legibilidad e inmutabilidad:

public class ID3 {

  private String header;

  private String title; // Required
  
  private String artist; // Required
  
  private String album;
  
  private int year;
  
  private String comment;
  
  private boolean zeroByte;
  
  private int track;
  
  private int genre;

  public static class Builder {
    
    // Required parameters
    private final String title;
    private final String artist;
    
    // Optional parameters
    private String header;
    private String album;
    private int year;
    private String comment;
    private boolean zeroByte;
    private int track;
    private int genre;
    
    public Builder(String title, String artist) {
      this.title = title;
      this.artist = artist;
    }
    
    public Builder header(String header) {
      this.header = header;
      return this;
    }
    
    public Builder album(String album) {
      this.album = album;
      return this;
    }
    
    public Builder year(int year) {
      this.year = year;
      return this;
    }
    
    public Builder comment(String comment) {
      this.comment = comment;
      return this;
    }
    
    public Builder zeroByte(boolean zeroByte) {
      this.zeroByte = zeroByte;
      return this;
    }
    
    public Builder track(int track) {
      this.track = track;
      return this;
    }
    
    public Builder genre(int genre) {
      this.genre = genre;
      return this;
    }
    
    public ID3 build() {
      return new ID3(this);
    }
    
  }
  
  public ID3(Builder builder) {
    title = builder.title;
    artist = builder.artist;
    header = builder.header;
    album = builder.album;
    year = builder.year;
    comment = builder.comment;
    zeroByte = builder.zeroByte;
    track = builder.track;
    genre = builder.genre;
  }
  
  // ... Getters
    
}

La instanciación de la clase quedaría así:

ID3 id3 = new ID3.Builder("Billie Jean", "Michael Jackson").header("TAG").

     album("Thriller").year(2009).comment("").zeroByte(false).track(6).genre(31);


Este artículo esta basado en el "Item" 2 del libro "Effective Java 2nd Edition" de Joshua Bloch.

One Comment

Leave a Comment
  1. Yo mero / Jul 28 2010 1:22 pm

    Nice explanation, fresh and clean

    Keep on it

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: