Table of Contents

Cómo diseñar un problema con POO

Definir las clases para nuestro problema

  1. Para cada elemento que aparezca en el problema que queremos solucionar, diseñaremos una clase con tantos atributos como característica tenga (no confundir con los posibles valores de esas características) y tantos métodos como operaciones o tareas puedan realizarse sobre ese elemento.
  2. Por lo general (y por ahora) declararemos todas las clases como públicas y cada una se escribirá en un fichero con el mismo nombre que la clase (y la extensión .java)
  3. Por lo general (y sino se demuestra lo contrario) todos los atributos de la clase serán privados (encapsulamiento)
  4. Diseñar al menos un constructor o bien algunos con las combinaciones de atributos más habituales a la hora de inicializar los objetos de la clase

Siempre será más conveniente un método que haga cambios sobre un atributo de la clase que modificar directamente dicho atributo

Encapsular la funcionalidad

En la medida de lo posible, una clase debe encapsular la funcionalidad de sus objetos lo máximo posible.

Siempre será mejor hacer un método que modifique una serie de atributos que tenerla que hacer en la clase principal o invocando a varios de los setters de la misma clase

public class Vehiculo { 
  . . .
  public void reparar() { 
    averiado = false;
    estado = Estado.LISTO; 
  }
 
  public void setAveriado( . . .) {
    . . .
  }
 
  public void setEstado( . . . } {
    . . .
  }
 
  . . .
}
. . .
Vehiculo coche = new Vehiculo(. . .)
. . .
coche.reparar();
. . .
coche.setAveriado(false);
. . .
coche.setEstado(Estado.LISTO);
. . .

Relaciones entre clases

Si queremos establecer una relación entre dos clases, haremos que cada una de ellas tenga un atributo de la otra:

public class Vehiculo {
  private String matricula;
  . . .
  private Propietario propietario;
}
public class Propietarrio {
  private String dni;
  . . . 
  private Vehiculo vehiculo;
}

Sobrescribir equals, hashCode y toString

equals

Es un método que podemos sobrescribir para decidir cuando dos objetos de una misma clase se consideran iguales. El funcionamiento por defecto es que dos objetos son el mismo si ocupan la misma zona de memoria pero en la práctica nos pueden interesar, por ejemplo, que dos objetos vehículo sean iguales si son iguales sus matrículas (o cualquier otro criterio), y poder compararlos directamente sin necesidad de conocer cuál es dicho criterio

public class Vehiculo {
  . . .
  @Override
  public boolean equals(Object object) {
    if (object == null) return false;
 
    if (object instanceof Vehiculo vehiculo) {
      if (vehiculo.getMatricula().equals(matricula))
        return true;
      else
        return false:
    }
 
    return false;
  }
}
. . .
if (unCoche.equals(otroCoche)) {
  . . .
}
. . .

hashCode

Es un método preparado para devolver la representación como hash de un objeto. Para cada clase tendríamos que implementarlo, para sobrescribir la versión por defecto de este método, para que devuelva el hash de lo que nosotros consideremos que identifica a cada objeto. En este caso podríamos suponer que la matrícula es el campo que permite distinguir un vehículo de otro, por lo que será el único campo para el que tengamos que calcular el hash, y devolverlo.

Este método lo utilizan algunas estructuras y colecciones de Java para identificar, de la forma más eficaz, si 2 objetos se deben de considerar equivalentes (para no añadirlo, por ejemplo, a un Set de objetos donde, por definición, no se admiten elementos repetidos). Cualquier otro método podría ser “demasiado lento” y compremetería el rendimiento de estas estructuras de datos.

public class Vehiculo {
  . . .
  @Override
  public int hashCode() {
    return matricula.hashCode();
  }
}

toString

Es un método que podemos sobrescribir para decidir cómo representar un objeto cuando éste sólo pueda ser representado como una cadena de texto. Si, por ejemplo, queremos mostrar por pantalla un objeto, Java automáticamente invoca a este método para mostrar la representación como cadena del mismo, que por defecto será el nombre de la clase seguido de la dirección de memoria (poco interesante en la práctica)

public class Vehiculo {
  . . .
  @Override
  public String toString() {
    return matricula;
  }
  . . .
}

Ejercicios

  1. Formas parte del equipo de desarrollo de un videojuego de plataformas y eres el encargado de diseñar las clases del proyecto, que será programado en Java. También tienes que escribir el código de esas clases. El videojuego es un plataformas donde hay un personaje principal y una serie de enemigos que se distribuyen a lo largo de un número determinado de pantallas.
    1. Del personaje se debe almacenar el nombre (hay varios para elegir), la vida, los puntos que lleva y también el inventario de todos los objetos que lleva conseguidos. También habrá que saber siempre en que pantalla está el personaje.
    2. Cada tipo de enemigo tiene un nombre diferente, y además hay que almacenar su vida y la pantalla y momento en el que aparecen. Además, cada uno tiene una habilidad (para matarte) diferente
    3. Hay muchas pantallas y cada una tiene un nombre y una serie de objetos escondidos en ella (que pueden o no aparecer, dependiendo de cómo juegue el jugador)
    4. Como se ha comentado, el personaje va recopilando una serie de objetos a lo largo de la partida. De vez en cuando, utilizando esos objetos o combinándolos con ellos, puede conseguir puntos o vidas extra. Es interesante almacenar, para cada objeto, su imagen, el nombre y el efecto que producen en el personaje cuando se usan. Como algunos de ellos deben combinarse entre si para usarse, habrá que guardar esa información también.
  2. Diseña las clases de una aplicación que sirva como calendario, donde los diferentes usuarios de la misma puedan gestionar su tiempo utilizándola. La idea es que la aplicación tenga su propio sistemas de usuarios, de los que se almacenará el nombre de usuario, la contraseña, su email, su teléfono móvil, su dirección y el tipo de usuario (que podrá ser administrador, usuario o invitado).
    1. La idea principal de la aplicación es poder anotar tareas o eventos pendientes para que queden registrados y poder ser consultados posteriormente. Además, la aplicación avisará (si asi lo indica el usuario) cuando se acerque, por ejemplo, la fecha de un evento determinado.
    2. Se distingue entre tareas y eventos:
    3. Una tarea es algo que un usuario tiene que hacer. No tiene fecha asignada, simplemente un nombre y una descripción
    4. Un evento es algo que ocurrirá en una fecha determinada. Para cada uno almacenaremos el nombre, la descripción y el lugar. Se podrá configurar para que la aplicación avise al usuario de diferentes formas (por pantalla, mediante SMS o mediante correo electrónico) y con la antelación que el usuario indique.
    5. Además, los usuarios podrá tener registrados a sus contactos (con nombre, apellidos, email y teléfono) que podrán o no ser usuarios de la aplicación, de forma que podrán compartir con ellos tareas y/o eventos. Asi, resultará interesante saber qué contactos hay vinculados a un determinado evento y/o tarea porque también serán notificados cuando lo sea el usuario.
  3. Implementa también una pequeña aplicación por consola para probar, en la medida de lo posible, el funcionamiento de la aplicación diseñada en el primer punto:
    1. Puedes, por ejemplo, simular el inicio de la aplicación (login usuario/contraseña)
    2. Añadir tareas / eventos
    3. Consultar tareas / eventos
    4. Modificar tareas / eventos
    5. Eliminar tareas / eventos

© 2023 Santiago Faci