Cómo diseñar un problema con POO
Definir las clases para nuestro problema
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.
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)
Por lo general (y sino se demuestra lo contrario) todos los atributos de la clase serán privados (encapsulamiento)
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:
Las clases pueden hacerse referencia unas a otras. Es decir, en una clase pueden aparecer atributos cuyo tipo es a su vez otra clase
En una clase también pueden aparecer atributos cuya clase sea la propia clase
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
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.
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.
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
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)
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.
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).
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.
Se distingue entre tareas y eventos:
Una tarea es algo que un usuario tiene que hacer. No tiene fecha asignada, simplemente un nombre y una descripción
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.
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.
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:
Puedes, por ejemplo, simular el inicio de la aplicación (login usuario/contraseña)
Añadir tareas / eventos
Consultar tareas / eventos
Modificar tareas / eventos
Eliminar tareas / eventos
© 2023 Santiago Faci