Práctica 2: Herencia
Programación II
Abril 2024 (08/04/2024)
DLSI – Universidad de Alicante
Alicia Garrido Alenda
Normas generales
El plazo de entrega para esta práctica se abrirá el lunes 22 de abril a las 9:00 horas y se cerrará el
viernes 26 de abril a las 23:59 horas. No se admitirán entregas fuera de plazo.
Se debe entregar la práctica en un fichero comprimido de la siguiente manera:
1. Abrir un terminal.
2. Situarse en el directorio donde se encuentran los ficheros fuente (.java) de manera que al
ejecutar ls se muestren los ficheros que hemos creado para la práctica y ningún directorio.
3. Ejecutar:
tar cfvz practica2.tgz *.java
Normas generales comunes a todas las prácticas
1. La práctica se debe entregar exclusivamente a través del servidor de prácticas del departamen-
to de Lenguajes y Sistemas Informáticos, al que se puede acceder desde la página principal del
departamento (http://www.dlsi.ua.es, enlace “Entrega de prácticas”) o directamente en
http://pracdlsi.dlsi.ua.es.
Bajo ningún concepto se admitirán entregas de prácticas por otros medios (correo electrónico,
Campus Virtual, etc.).
El usuario y contraseña para entregar prácticas es el mismo que se utiliza en el Campus Virtual.
La práctica se puede entregar varias veces, pero sólo se corregirá la última entrega.
2. El programa debe poder ser compilado sin errores con el compilador de java existente en la distri-
bución de Linux de los laboratorios de prácticas; si la práctica no se puede compilar su calificación
será 0. Se recomienda compilar y probar la práctica con el autocorrector inmediatamente antes de
entregarla.
1
Universidad de Alicante
3. La práctica debe ser un trabajo original de la persona que entrega; en caso de detectarse indicios
de copia de una o más prácticas (o compartición de código) la calificación de la práctica será
0 y se enviará un informe al respecto tanto a la dirección del departamento como de la titulación.
4. Se recomienda que los ficheros fuente estén adecuadamente documentados, con comentarios donde
se considere necesario.
5. La primera corrección de la práctica se realizará de forma automática, por lo que es imprescindible
respetar estrictamente los formatos de salida que se indican en este enunciado.
6. El cálculo de la nota de la práctica y su influencia en la nota final de la asignatura se detallan en las
transparencias de la presentación de la asignatura.
7. Para evitar errores de compilación debido a codificación de caracteres que descuenta 0.5 de la nota
de la práctica, se recomienda utilizar EXCLUSIVAMENTE caracteres ASCII (el alfabeto inglés)
en vuestros ficheros, incluídos los comentarios. Es decir, no poner acentos, ni ñ, ni ç, etcétera.
8. El tiempo estimado por el corrector para ejecutar cada prueba debe ser suficiente para que finalice
su ejecución correctamente, en otro caso se invoca un proceso que obliga a la finalización de la
ejecución de esa prueba y se considera que dicha prueba no funciona correctamente.
2
Universidad de Alicante
Descripción del problema
El objetivo de esta práctica es asentar los conocimientos propios del paradigma de programación orien-
tada a objetos relativos a la herencia. Para ello en esta práctica se pide implementar una serie de clases,
algunas de ellas relacionadas con las implementadas en la práctica anterior, de las cuales se crearán objetos
que se relacionan entre sí de diferentes formas para comprender de forma práctica el funcionamiento de la
herencia en programación.
Con el paso del tiempo las aguas del planeta, tiempo atrás cristalinas, muestran rastros de una contami-
nación creciente, ya que algunos underwater empezaron a ser un tanto dejados con la limpieza y cuidado
de su planeta. El paso del tiempo y la evolución se encargaron de que aparecieran dos subespecies de
los underwater, los cleaners y los grimys. Un Cleaner es un underwater preocupado por su medioam-
biente con la capacidad de eliminar parte de la contaminación del planeta. Por su parte un Grimy es un
underwater desconsiderado y nada preocupado por su higiene, y mucho menos por la del planeta, lo que
ha propiciado la aparición de una nueva especie, los simbiontes, ya que le aparecen cuando llega a cierto
grado de dejadez. Un Symbiote es una especie de rémora que se asocia a un grimy, de manera que lo usa
para explorar las masas de agua y explotar sus recursos, contaminando las porciones de agua que explota
dando lugar a porciones de agua contaminadas, Contaminated. A cambio el grimy usa al simbionte
como reserva de energía extra, ya que puede obtener enegía de él.
Para esta práctica son necesarias todas las clases de la práctica anterior (Food, Water, BodyWater
y UnderWater) además de las clases que se definen a continuación y que es necesario implementar para
esta práctica.
Se pueden añadir variables de instancia y/o clase (siempre que sean privadas) y métodos cuando se
considere necesario (que pueden ser públicos o privados) a cualquiera de las clases.
1. Especificación de las clases
En Compound se establecen los compuestos contaminantes que pueden aparecer en el agua junto
con su peso molecular, por tanto será un tipo enumerado en el que se definirán los tipos de objetos junto
con su valor:
ANILINE 93.13
METANOL 32.04
LACTATE 90.08
GLUCOSE 180.15
LACTOSE 342.31
El enumerado Compound tiene una variable de instancia:
> weight: indica el peso molecular del compuesto (double);
y los siguientes métodos:
⊙ Compound: constructor al que se le pasa por parámetro un número real para su peso molecular.
⊙ getWeight: devuelve su peso molecular.
3
Universidad de Alicante
Un Contaminated es un Water, por tanto tiene todas sus características, pero además tiene la va-
riable de instancia:
> compounds: array dinámico con los compuestos contaminantes presentes en la porción de agua
(ArrayList<Compound>).
Un objeto Contaminated puede realizar las mismas acciones que un objeto Water, pero algunas de
forma distinta:
⊙ constructor: se le pasan por parámetro dos números reales, el primero para la temperatura y el
segundo para su salinidad1. Inicialmente no tiene compuestos.
⊙ increase: se suma 1 más la parte decimal del peso molecular de todos sus compuestos y se divide
la temperatura por esta suma. El resultado de la división es la cantidad con la que se incrementa el
volumen de su alimento si lo tiene. El método devuelve cierto si se ha producido un incremento en
el volumen del alimento, y falso en cualquier otro caso.
⊙ decrease: se suma la parte entera del peso molecular de todos sus componentes y se obtiene el
valor absoluto de restar a la salinidad esta suma, con el que se decrementa el volumen de su alimento
si lo tiene. El método devuelve cierto si se ha producido un decremento en el volumen del alimento,
y falso en cualquier otro caso.
Y los siguientes métodos:
⊙ pollute: se le pasa por parámetro un entero. Si el entero pasado por parámetro se corresponde con
una posición válida del enumerado Compound, se añade el compuesto que ocupa dicha posición
al principio del array compounds. El método devuelve el número de compuestos que contiene el
array compounds.
⊙ purge: se le pasa por parámetro una cadena. Busca entre sus compuestos aquellos cuyo nombre
coincide, ignorando diferencias entre mayúsculas y minúsculas, con la cadena pasada por parámetro.
Si los encuentra los elimina de sus compuestos y devuelve cierto. En cualquier otro caso devuelve
falso.
⊙ getCompounds: devuelve un array con el nombre de los compuestos que tiene.
Un Symbiote es una nueva especie que ha aparecido recientemente en el planeta asociado a una de
las evoluciones de los underwater y cuyas variables de instancia son:
> storage: energía almacenada por el simbionte (double);
> associated: underwater con el que está asociado (Grimy);
> place: orden en el que fue creado el simbionte (int).
Y tiene una variable de clase indica el número de simbiontes creados:
> counter: contador que indica el número de simbiontes creados hasta el momento. Se inicializa a
1 (static int).
1
Se cumplen las mismas restricciones para estos valores que en la práctica 1.
4
Universidad de Alicante
Y los siguientes métodos:
⊙ constructor: se le pasa por parámetro un underwater. Si el underwater es un grimy, se convierte
en su asociado siempre que el grimy no tenga ningún simbionte asociado. Inicialmente su energía
almacenada es 0 y el orden en el que fue creado es el valor actual del contador. A continuación se
incrementa el valor del contador.
⊙ eat: se le pasa por parámetro un alimento del que obtiene la energía y con ella incrementa su
energía almacenada.
⊙ explore: se le pasa por parámetro una cadena. Accede a la masa de agua de su asociado y busca
en todas sus posiciones el alimento cuyo nombre coincide2 con la cadena pasada por parámetro, de
manera que cuando lo encuentra:
• obtiene el alimento, obtiene la energía del alimento y con ella incrementa su energía almace-
nada;
• si la porción de agua en la que estaba el alimento no estaba contaminada, se sustituye esta
porción de agua por una contaminada, que tendrá las mismas características que la original;
• en cualquier caso, a continuación se contamina la porción de agua contaminada con el com-
puesto que ocupa la posición obtenida la realizar el cálculo:
position = (i + j) %(n)
donde i y j son la fila y columna, respectivamente, de la porción de agua y n es el número de
compuestos contaminantes existentes.
El método devuelve el número de porciones de agua que se han contaminado por primera vez.
⊙ getEnergy: se le pasa por parámetro un grimy y un número real. Si el número real pasado por
parámetro es mayor que 0 y el grimy pasado por parámetro es el asociado del simbionte, el simbionte
resta de su energía almacenada la cantidad indicada por el parámetro y la devuelve. Si la cantidad de
energía pedida es mayor que la almacenada, el simbionte actualiza su energía almacenada restándole
su parte entera y devolviéndola. En cualquier otro caso, el método devuelve 0.
⊙ setAssociated: se le pasa por parámetro un grimy. Si ninguno de los dos tiene asociado3 se
establece la relación simbiótica entre ambos4 y devuelve cierto. En cualquier otro caso devuelve
falso.
⊙ getStorage: devuelve la energía almacenada.
⊙ getAssociated: devuelve a su asociado.
⊙ getPlace: devuelve el orden en el que fue creado.
⊙ getCounter: método de clase que devuelve el contador de simbiontes.
2
Ignorando diferencias entre mayúsculas y minúsculas
3
Es decir, el simbionte no tiene un grimiy asociado ni el grimy tiene a su vez un simbionte asociado.
4
Es decir, el grimy se convierte en el asociado del simbionte y el simbionte en el asociado del grimy pasado por parámetro.
5
Universidad de Alicante
En Oddity se establecen las posibles rarezas que pueden tener las nuevas subespecies de underwater,
por tanto es un tipo enumerado en el que se definen dichas rarezas, que son las siguientes:
COLORLESS,ODORLESS,TRANSLUCENT,LUMINISCENT,VEGETARIAN,CARNIVOROUS
Un Grimy es un UnderWater, por tanto tiene todas sus características, pero además tiene las varia-
bles de instancia:
> associated: simbionte asociado (Symbiote);
> oddities: array dinámico con las rarezas del grimy (ArrayList<Oddity>);
Un Grimy puede realizar las mismas acciones que un UnderWater, pero algunas de forma distinta:
⊙ constructor: se le pasa por parámetro una cadena para su nombre y un array no dinámico de enteros
que representan sus rarezas. El constructor debe hacer lo mismo que el constructor de UnderWater,
pero además para cada entero del array pasado por parámetro hay que comprobar si se corresponde
con una posición de Oddity y en ese caso añadir el objeto Oddity correspondiente a esa po-
sición al final de su variable oddities, sin que haya elementos repetidos. Inicialmente no tiene
simbionte asociado.
⊙ foodSearch: se le pasan por parámetro dos enteros que indican la posición de una porción de agua
de la masa de agua en la que está, el primero indica la fila y el segundo la columna. El grimy obtiene
directamente el alimento que contiene esta porción de agua sin tener en cuenta intolerancias ni nada,
y obtiene la energía del alimento. Si la porción de agua está contaminada se le resta a la energía
obtenida el número de compuestos contaminantes que tiene la porción de agua y si el resultado es
mayor que 0 se suma a su fuerza. Si la porción de agua no está contaminada suma directamente a
su fuerza la energía obtenida del alimento. Finalmente, haya obtenido energía o no, si su fuerza es
mayor que la suma de los dos enteros pasados por parámetro y no tiene simbionte asociado, se crea
un simbionte que tendrá como asociado al grimy, de la misma manera que el nuevo simbionte se
convierte en el asociado del grimy, y se le resta al grimy la mitad de su fuerza. El método devuelve
cierto si se ha incrementado la fuerza del grimy al final del proceso, y falso en cualquier otro caso.
⊙ scan: antes de realizar la acción el grimy obtiene toda la energía que puede de su simbionte su-
mándola a su propia fuerza y, a continuación, realiza la acción igual que cualquier underwater por
tanto devolverá un array dinámico con una copia de los alimentos encontrados con los que no tiene
intolerancias, o null si no encuentra ninguno.
Y los siguientes métodos:
⊙ setAssociated: se le pasa por parámetro un simbionte. Si el grimy no tiene simbionte asociado
y el simbionte pasado por parámetro tiene como asociado al propio grimy, se termina de establecer
la relación simbiótica y el simbionte pasado por parámetro se convierte en el asociado del grimy.
⊙ match: se le pasa por parámetro un underwater. Si el underwater pasado por parámetro no es él
mismo5 y ambos se encuentran en la misma masa de agua, se realiza un estudio de compatibilidad.
El estudio de compatibilidad consiste en:
5
Es un objeto distinto de this.
6
Universidad de Alicante
• si son de la misma subespecie suma un punto
• si tienen rarezas en común, suman un punto por cada rareza en común
• si el underwater pasado por parámetro es un cleaner que está emparejado resta cuatro puntos
• si suman dos o más puntos hay compatibilidad
Si la fuerza no es superior a 1 para alguno de estos underwater, y se trata de un grimy, éste intentará
obtener de su simbionte asociado 1.5 de energía para intentar garantizar el emparejamiento. Si hay
compatibilidad y la fuerza de ambos es superior a 1, se emparejan temporalmente para crear un
nuevo underwater que será un grimy cuyo nombre estará formado por el nombre del underwater
pasado por parámetro, un guión (-) y el nombre del propio grimy y tendrá las rarezas del propio
grimy y ambos underwater decrementan su fuerza en una unidad. Además si hay compatibilidad
y el underwater pasado por parámetro es un cleaner, el grimy pierde su simbionte si lo tuviera,
obteniendo antes su energía almacenada y repartiéndola a medias con el cleaner. Finalmente el
método devuelve el nuevo grimy si se ha creado, o null en caso contrario.
⊙ move: el grimy cambia de masa de agua a la situada en la hidrosfera a la izquierda de su masa de
agua actual si es posible, es decir si existe una masa de agua a la izquierda (una posición anterior en
el array). El método devuelve cierto si realiza el cambio, y falso en otro caso.
⊙ getAssociated: devuelve su simbionte asociado.
⊙ getOddities: devuelve un array con los nombres de sus rarezas en el mismo orden en el que se
encuentran en su variable oddities.
Un Cleaner es un UnderWater, por tanto tiene todas sus características, pero además tiene la va-
riable de instancia:
> oddities: array dinámico con las rarezas del cleaner (ArrayList<Oddity>);
> partner: pareja del cleaner (Cleaner);
Un Cleaner puede realizar las mismas acciones que un UnderWater, pero algunas de forma distinta:
⊙ constructor: se le pasa por parámetro una cadena para su nombre y un array dinámico de cadenas que
representan sus rarezas. El constructor debe hacer lo mismo que el constructor de UnderWater,
pero además para cada cadena del array pasado por parámetro hay que comprobar si se corresponde
con el nombre, ignorando diferencias entre mayúsculas y minúsculas, de una Oddity y en ese
caso añadir el objeto Oddity correspondiente a esa cadena a su variable oddities, sin que haya
elementos repetidos.
⊙ foodSearch: se le pasan por parámetro dos enteros que indican la posición de una porción de
agua en la masa de agua en la que está, el primero indica la fila y el segundo la columna. El cleaner
comprueba si la porción de agua indicada está contaminada, y si es así elimina de ella todos los
compuestos contaminantes que puede de ella, ya que eliminar un compuesto decrementa su fuerza
en una unidad y la fuerza no puede ser negativa. A continuación el cleaner, tanto si ha eliminado
contaminantes como si no, consulta esta posición de la masa de agua en la que está actualmente de
manera que si no tiene intolerancias6 con el alimento contenido, se obtiene el alimento y se obtiene
6
El nombre del alimento no aparece en las intolerancias del underwater ignorando diferencias entre mayúsculas y minúscu-
las.
7
Universidad de Alicante
la energía del alimento, que se suma a su fuerza. El método devuelve cierto si se ha incrementado la
fuerza del cleaner al final del proceso, y falso en cualquier otro caso.
Y los siguientes métodos:
⊙ match: se le pasa por parámetro un underwater. Si el underwater pasado por parámetro no es él
mismo7 y ambos se encuentran en la misma masa de agua, se realiza un estudio de compatibilidad.
El estudio de compatibilidad consiste en:
• si son de la misma subespecie suma un punto;
• si tienen rarezas en común, suman un punto por cada rareza en común;
• si alguno de los underwater está emparejado resta tres puntos cada pareja, si la pareja no es con
quien está haciendo match en el momento. Si está haciendo match con su pareja suma cinco
puntos;
• si suman dos o más puntos hay compatibilidad
Si hay compatibilidad y la fuerza de ambos es superior a 1, se emparejan de la siguiente manera:
1. si el underwater pasado por parámetro es un grimy, se emparejan temporalmente, es decir el
cleaner no actualiza su variable partner, el grimy pierde su simbionte si lo tuviera, obte-
niendo antes su energía almacenada y cediéndosela al cleaner, y se crea un nuevo underwater
que será un cleaner cuyo nombre estará formado por el nombre del underwater pasado por
parámetro, un guión (-) y el nombre del propio cleaner y tendrá las rarezas del propio cleaner
y ambos underwater decrementan su fuerza en una unidad.
2. si el underwater pasado por parámetro es un cleaner, se empareja definitivamente, es decir
actualizan su variable partner ambos cleaner y si alguno tuviera una pareja anterior, esta
pareja anterior dejará de tener pareja, y se crea un nuevo underwater que será un cleaner cuyo
nombre estará formado por el nombre del propio cleaner, un signo de suma (+) y el nombre
del cleaner pasado por parámetro, tendrá las rarezas del cleaner pasado por parámetro y ambos
cleaner decrementan su fuerza en una unidad.
Finalmente el método devuelve el nuevo cleaner si se ha creado, o null en caso contrario.
⊙ move: el cleaner cambia de masa de agua a la situada en la hidrosfera a la derecha de su masa de
agua actual si es posible, es decir si existe una masa de agua a la derecha (una posición posterior en
el array). El método devuelve la posición que ocupa en la hidrosfera la masa de agua en la que está
actualmente el cleaner. Por defecto devuelve -1.
⊙ clean: el cleaner hace una batida en la masa de agua en la que está actualmente para buscar por-
ciones de agua contaminada. Cuando encuentra una comprueba si tiene compuestos contaminantes,
y si no tiene ninguno reemplaza esta porción de agua contaminada por una porción de agua limpia
que tendrá la misma temperatura, misma salinidad y mismo alimento si lo tiene que la contaminada.
El método devuelve la cantidad de porciones de agua contaminada que han sido reemplazadas por
porciones de agua limpia.
⊙ getPartner: devuelve su pareja.
⊙ getOddities: devuelve un array con los nombres de sus rarezas en el mismo orden en el que se
encuentran en su variable oddities.
7
Es un objeto distinto de this.
8
Universidad de Alicante
Restricciones en la implementación
⊛ Los métodos deben ser públicos y tener una signatura concreta:
• En Compound
◦ private Compound(double)
◦ public double getWeight()
• En Contaminated
◦ public Contaminated(double,double)
◦ public boolean increase()
◦ public boolean decrease()
◦ public int pollute(int)
◦ public boolean purge(String)
◦ public String[] getCompounds()
• En Symbiote
◦ public Symbiote(UnderWater)
◦ public void eat(Food)
◦ public int explore(String)
◦ public double getEnergy(Grimy,double)
◦ public double getStorage()
◦ public boolean setAssociated(Grimy)
◦ public Grimy getAssociated()
◦ public int getPlace()
◦ public static int getCounter()
• En Grimy
◦ public Grimy(String,int[])
◦ public boolean foodSearch(int,int)
◦ public ArrayList<Food> scan()
◦ public void setAssociated(Symbiote)
◦ public Grimy match(UnderWater)
◦ public boolean move()
◦ public Symbiote getAssociated()
◦ public String[] getOddities()
• En Cleaner
◦ public Cleaner(String,ArrayList<String>)
◦ public boolean foodSearch(int,int)
◦ public Cleaner match(UnderWater)
◦ public int move()
◦ public int clean()
◦ public Cleaner getPartner()
◦ public String[] getOddities()
⊛ Ninguno de los ficheros entregados en esta práctica debe contener un método public static
void main(String[] args).
9
Universidad de Alicante
⊛ Todas las variables de instancia y de clase deben ser privadas. En otro caso se restará un 0.5 de la
nota total de la práctica.
Probar la práctica
En Materiales Docentes del UaCloud se publicará un corrector de la práctica con un conjunto míni-
mo de pruebas (se recomienda realizar pruebas más exhaustivas de la práctica).
Podéis enviar vuestras pruebas (fichero con extensión java, con un método main y sin errores de
compilación) a los profesores de la asignatura mediante tutorías de UACloud, para obtener la salida
correcta a esa prueba a partir del 28 de marzo. En ningún caso se modificará/corregirá el código
de las pruebas. Los profesores contestarán a vuestra tutoría adjuntando la salida de vuestro main,
si no da errores.
El corrector viene en un archivo comprimido llamado correctorP2.tgz. Para descomprimirlo
se debe copiar este archivo donde queramos realizar las pruebas de nuestra práctica y ejecutar:
tar xfvz correctorP2.tgz
De esta manera se creará una carpeta publicar que contendrá:
• El fichero corrige.sh: shell-script que tenéis que ejecutar.
• El directorio practica2-pruebas: dentro de este directorio están los ficheros con exten-
sión .java, programas en Java con un método main que realiza una serie de pruebas sobre la
práctica, y los ficheros con el mismo nombre pero con extensión .txt con la salida correcta
para la prueba correspondiente.
Una vez que tenemos esto, debemos copiar nuestros ficheros fuente (sólo aquellos con extensión
.java) al directorio publicar que se ha creado, donde está el fichero corrige.sh.
Sólo nos queda ejecutar el shell-script. Primero debemos darle permisos de ejecución si no los tiene.
Para ello ejecutar:
chmod +x corrige.sh
corrige.sh
Solo es necesario hacerlo la primera vez que se descomprime.
Una vez se ejecuta corrige.sh los ficheros que se generarán son:
• errores.compilacion: este fichero sólo se genera si el corrector emite por pantalla el
mensaje “Error de compilacion: 0” y contiene los errores de compilación resultado
de compilar los fuentes de una práctica particular. Para consultar el contenido de este fichero
se puede abrir con cualquier editor de textos.
• Ficheros con extensión .tmp.err: estos ficheros debe estar vacíos por regla general. Só-
lo contendrán información si el corrector emite el mensaje “Prueba p01: Error de
ejecucion”, por ejemplo para la prueba p01, y contendrá los errores de ejecución pro-
ducidos al ejecutar el fuente p01 con los ficheros de una práctica particular.
10
Universidad de Alicante
• Ficheros con extensión .tmp: ficheros de texto con la salida generada por pantalla al ejecutar
el fuente correspondiente, por ejemplo p01.tmp contendrá la salida generada al ejecutar el
programa p01 con los ficheros de una práctica particular.
Si en la prueba no sale el mensaje “Prueba p01: Ok”, por ejemplo para la prueba p01, o algún
otro de los comentados anteriormente, significa que hay diferencias en las salidas, por tanto se debe
comprobar que diferencias puede haber entre los ficheros p01.txt y p01.tmp. Para ello ejecutar
en línea de comando, dentro del directorio practica2-pruebas, la orden: diff p01.txt
p01.tmp
11