Programación Orientada a Objetos: Composición

4 minuto(s) de lectura

Este Post es la sexta parte de la serie “Programación Orientada a Objetos”. Esta serie tiene como objetivo establecer los fundamentos de la POO, utilizando Java como ejemplo. Los temas de esta serie son los siguientes:

  1. Definición de POO
  2. Clases y Objetos
  3. Herencia
  4. Abstracción
  5. Encapsulamiento
  6. Polimorfismo
  7. Composición
  8. Principios utilizados en la POO

Todo el código de la serie está disponible en GitHub.

El código correspondiente a esta quinta parte lo pueden encontrar aquí.

Todos los ejemplos están hechos en Netbeans.


La composición es una técnica de programación que se utiliza para implementar objetos que contengan variables de instancia que hagan referencia a otros objetos. Se implementa una relación de un objeto con otras clases. Esta técnica se utiliza, al igual que la herencia, para reutilizar código, con la diferencia de que aquí también se centraliza código común en un objeto particular.

En Java y en POO, se prefiere implementar composición en lugar de herencia. Esto es debido a que la herencia hace que nuestro objeto dependa estrechamente de la implementación de la clase padre, y, si ésta llega a cambiar, nuestro objeto podría tener comportamiento erróneo y tendría que ser modificado también.

En cambio con la composición, nuestro objeto cuenta con variables de instancia de otros objetos por lo que la implementación de los demás objetos podría cambiar y nuestro código no tendría que ser modificado, mientras la interfaz que se utiliza no sea modificada.

Otra ventanja, es que al utilizar herencia, nuestra clase hija obtiene todas las implementaciones públicas que tenga la clase padre. En cambio con la composición, nuestra clase tiene que mandar llamar de manera explícita los métodos del objeto en cuestión.

De igual manera, al utilizar la composición, los objetos que se tengan en la clase pueden ser creados hasta el momento de ser utilizados. Con la herencia, desde el inicio se tiene todo el código disponible de la clase padre.

Por último, se pueden tener diferentes objetos dentro de la clase, cada uno implementando una funcionalidad en específico. En cambio con la herencia, solamente se puede heredar de una sola clase.

Veamos un ejemplo. Tendremos una clasae Usuario, la cual tendrá un objeto Trabajo y un objeto Dirección. De igual manera la clase Trabajo tendrá un objeto Dirección.

Empezemos con la clase Direccion. Solamente tendrá variables de tipo String para guardar toda la información de una dirección:

public class Direccion {
  private String calle;
  private String numero;
  private String colonia;
  private String codigoPostal;

  public Direccion(String calle, String numero, String colonia, String codigoPostal) {
    this.calle = calle;
    this.numero = numero;
    this.colonia = colonia;
    this.codigoPostal = codigoPostal;
  }

  public void imprimirDireccion() {
    System.out.println(calle + " número " + numero + ", colonia: " + colonia + ", C.P. " + codigoPostal);
  }
}

Crearemos ahora la clase Trabajo. La clase trabajo tendrá un objeto de tipo Dirección, con esto podrá hacer uso de los métodos de Dirección.

public class Trabajo {
  private String puesto;
  private int antiguedad;

  private Direccion direccion;

  public Trabajo(String puesto, int antiguedad, Direccion direccion) {
    this.puesto = puesto;
    this.antiguedad = antiguedad;
    this.direccion = direccion;
  }

  public String getPuesto() {
    return puesto;
  }

  public int getAntiguedad() {
    return antiguedad;
  }
}

Vamos a crear un método en la clase Trabajo que haga uso del método imprimirDirección() de la clase Dirección. Este es un ejemplo del uso de la Composición.

public void imprimirDireccion() { direccion.imprimirDireccion(); }

Crearemos ahora la clase Usuario, que tendrá un objeto Trabajo y un objeto Dirección, además de las variables para la información básica de un usuario.

public class Usuario {
  private String nombre;
  private String email;
  private int edad;

  private Direccion direccion;
  private Trabajo trabajo;

  public Usuario(String nombre, String email, int edad) {
    this.nombre = nombre;
    this.email = email;
    this.edad = edad;
  }

  public void setTrabajo(Trabajo trabajo) {
    this.trabajo = trabajo;
  }

  public void setDireccion(Direccion direccion) {
    this.direccion = direccion;
  }

  public String getNombre() {
    return nombre;
  }

  public String getEmail() {
    return email;
  }

  public int getEdad() {
    return edad;
  }
}

Ahora haremos uso de la Composición, al utilizar los métodos de la clase Trabajo y de la clase Dirección:

public void imprimirDireccion() {
  System.out.println("========= DIRECCION USUARIO ========");
  if (direccion != null) {
    direccion.imprimirDireccion();
  } else {
    System.out.println("No hay dirección agregada.");
  }
}

public void imprimirTrabajo() {
  System.out.println("========= TRABAJO USUARIO ========");
  if (trabajo != null) {
    System.out.println("Trabajo: " + trabajo.getPuesto());
    System.out.println("Direccion de trabajo: ");
    trabajo.imprimirDireccion();
  } else {
    System.out.println("No tiene trabajo");
  }
}

Podemos ver cómo la clase Usuario utiliza el concepto de delegación ya que delega a los objetos dirección y trabajo la responsabilidad de imprimir la dirección de cada uno. A su vez, el objeto Trabajo delega esta funcionalidad al objeto Dirección.

Ahora haremos uso del objeto Usuario en nuestro método main:

Direccion direccionTrabajo = new Direccion("Calle trabajo", "32", "Colonia trabajo", "123456");
Trabajo trabajo = new Trabajo("Software Developer", 5, direccionTrabajo);

Direccion direccionCasa = new Direccion("Calle casa", "18", "Colonia casa", "654321");
Usuario usuario = new Usuario("Warrior Minds", "email@gmail.com", 25);
usuario.setDireccion(direccionCasa);
usuario.setTrabajo(trabajo);

usuario.imprimirDireccion();
usuario.imprimirTrabajo();

Podemos ver cómo es mejor tener varias variables con referencias a distintos objetos: se pueden agregar las que sean necesarias, algo que no se puede hacer con la herencia. De igual manera, si algo cambia en Dirección o Trabajo, solamente el objeto Usuario tendría que ser modificado y no nuestro “cliente” que en este caso es el método main, ya que para cualquiera que utilice la clase Usuario, solamente tendrá que mandar llamar los métodos imprimirDireccion() e imprimirTrabajo() y la clase Usuario será la encargada de mandar llamar los métodos necesarios de los objetos que la componen.

Etiquetas:

Categorías:

Actualizado:

Deja un comentario