Java - Necesito ayuda con este problema

 
Vista:
Imágen de perfil de Kabuto
Val: 3.428
Oro
Ha mantenido su posición en Java (en relación al último mes)
Gráfica de Java

Necesito ayuda con este problema

Publicado por Kabuto (1381 intervenciones) el 06/07/2021 01:46:11
Hola.
Veamos.

El objetivo es hacer que este programa funcione.
Programa que es lo primero que has de escribir, aunque no va a funcionar porque tenemos que completar antes todo lo que se nos pide.
Los números escritos como comentarios, son los resultados que deberíamos obtener en pantalla.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FunctionTester {
 
	public static void main(String[] args) {
		Function f1 = new LinearFunction(2, 0);
		Function f2 = new QuadraticFunction(1, 0 , 0);
		Function f3 = new CompositeFunction(f1, f2);
		System.out.println(f3.evaluate(1)); //4.0
		System.out.println(f3.evaluate(2)); //16.0
		Function f4 = new SineFunction();
		Function f5 = new CompositeFunction(f1, f4);
		Function f6 = new CompositeFunction(f5, f1);
		System.out.println(f6.evaluate(0)); //0.0
		System.out.println(f6.evaluate(Math.PI / 4.0)); //2.0
	}
 
}

Si miramos este código, vemos que hay varios objetos Function, pero no son todos iguales: hay un LinearFunction, un QuadraticFunction, un SineFunction y varios CompositeFunction

Si son clases distintas, ¿por qué todas pueden ser instanciadas como Function?

Porque todas implementan la interfaz Function
1
2
3
public interface Function {
	double evaluate(double x);
}

Si te fijas, una interface se parece a una clase, pero no es lo mismo. En una interface, no "hay código".
Ahí vemos que se declara (pero no se define) un método llamado evaluate()

Esto significa que cualquier clase que vaya a implementar esta interfaz, automáticamente tendrá ese método evaluate(), pero dicha clase tendrá que decidir cómo quiere que funcione ese método. Es decir, tendrá que definirlo.

Por eso, aunque este programa va a tener varias clases que van a compartir ese método evaluate(), dicho método se comportará diferente en cada clase, porque cada clase lo va a definir a su manera.

No se si hasta aquí me vas siguiendo....

Bien, entonces, hay que crear las clases que van a implementar la interfaz Function para que cumplan su cometido y hacer que funcione el programa principal.

El enunciado ya nos dice como sería la clase QuadraticFunction
Que es una clase capaz de evaluar (evaluate()) una función cuadrática de una variable.

Los atributos a, b, y c son los coeficientes, que se indican mediante el constructor de la clase.

Y la variable para completar el polinomio, es el argumento llamado x que recibe la función evaluate() entre los paréntesis.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class QuadraticFunction implements Function{
 
	private double a,b,c; //Coeficientes
 
	public QuadraticFunction(double a, double b, double c) {
		this.a = a;
		this.b = b;
		this.c = c;
	}
 
	@Override
	public double evaluate(double x) { //x es la variable del polinomio cuadrático
		return a * Math.pow(x, 2) + b*x + c;
	}
 
}

El enunciado también nos dice como es la clase SineFunction, que básicamente lo que hace es calcular el seno de un valor.
Este valor lo recibe el método evaluate()
La clase no tiene atributos, porque no los necesita, se trata de una función muy simple y sencilla:
1
2
3
4
5
6
7
8
public class SineFunction implements Function{
 
	@Override
	public double evaluate(double x) {
		return Math.sin(x); //Retorna el seno de x
	}
 
}

Bien, hasta aquí, no hemos hecho nada por nosotros mismos, todo esto es código que ya nos proporciona el enunciado.
Ahora viene la parte en la que nos toca a nosotros trabajar.

Nos piden crear dos clases:

LinearFunction, que representará una función lineal --> y = ax + b
CompositeFunction, que representará la composición de dos funciones. Es decir, debe poder recibir dos Function cualesquiera para realizar una composición con ellas.

Empecemos por lo fácil, hemos dicho que:
1
LinearFunction, que representará una función lineal --> y = ax + b

Esto significa que tenemos que crear una clase con dos atributos: a y b
Estos atributos recibirán valores por constructor.
Al implementar la interfaz Function, tendremos un método evaluate() al que debemos decirle como debe operar con la variable x que recibe entre paréntesis.

Y lo que tiene que hacer es multiplicar a por x y sumarlo a b --> a*x + b

Intenta escribir tú dicha clase. No importa si no lo consigues o te equivocas o lo que sea... Solo con intentarlo ya estás aprendiendo, lo creas o no.

Tras terminar tu intento, compáralo con el código que pongo a continuación:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class LinearFunction implements Function {
 
	private double a,b;
 
	public LinearFunction(double a, double b) {
		this.a = a;
		this.b = b;
	}
 
	@Override
	public double evaluate(double x) {
		return a*x + b;
	}
 
}

Si tu código ha quedado igual o equivalente al mío, enhorabuena.
Si no, no pasa nada. Corrige lo que sea necesario y pregunta si no entiendes el por qué de algo.

Bueno, ahora toca lo difícil, la clase
1
CompositeFunction

Esta clase hemos dicho que es la "composición" de dos funciones.
O sea, que sus atributos serán dos objetos Function (que en realidad pueden ser LinearFunction, SineFunction, etc...

Estos atributos recibirán las funciones correspondientes por constructor

Ha de implementar la interface Function, así que también tendrá un método evaluate()

Y ahora viene lo "difícil", ¿qué ha de evaluar esta función cuyos atributos son también funciones?

Bien, para saber lo que ha de hacer, sería interesante que tú mismo hicieras una búsqueda en Internet intentando averiguar cómo se hace o qué es "una composición de funciones", de dos funciones en nuestro caso.
Seguramente encontrarás resultados que muestran un montón de fórmulas tan bonitas como complejas e incomprensibles para los que no somos matemáticos....
Aún así intenta averiguarlo por tu cuenta y luego sigues leyendo este post.

Si lo has conseguido, bien.
Si no lo has conseguido entender, te lo explico yo ahora en cristiano...
Básicamente, una composición de dos funciones consiste en que un valor/variable primero lo evalua una de las funciones, y el resultado que origina, es ahora evaluado por la segunda función, que es quien origina ya el resultado final.

Es decir, que el método evaluate() de nuestra clase ha de hacer que la x que recibe entre paréntesis, la reciba el evaluate() de su primer atributo Function.
El resultado que obtenga, acto seguido lo recibe el evaluate() del segundo atributo Function.
Y este último resultado obtenido, es lo que ha de retorna el evaluate() de la clase CompositeFunction

En realidad, es más fácil de escribir el código que explicarlo, je je.

Igual que antes, intenta por tu cuenta escribir esta clase. Llega hasta donde puedas y luego comparas con la que pongo a continuación.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class CompositeFunction implements Function{
 
	private Function funcion1, funcion2;
 
	public CompositeFunction(Function funcion1, Function funcion2) {
		this.funcion1 = funcion1;
		this.funcion2 = funcion2;
	}
 
 
	@Override
	public double evaluate(double x) {
		return funcion2.evaluate(funcion1.evaluate(x)); //Primero evalúa función 1, y su resultado lo evalúa función 2
	}
 
}

Como hemos dicho, esta clase posee dos Function como atributos y lo que evalúa es el resultado de una variable que ha pasado por esas dos funciones de las que se compone.

Con esto, ya podemos ejecutar el programa principal, y comprobar que los resultados en pantalla coinciden con los indicados en los comentarios del código.

1
2
3
4
4.0
16.0
0.0
2.0

Y listo, espero que hayas podido entenderlo bien. Pregunta cualquier cosa que no te haya quedado claro.

Observa que la intención de este ejercicio es demostrar las características de las interfaces.
Todas las clases tienen un método evaluate() porque todas implementan Function, pero este método es completamente distinto para cada clase.

Porque las interfaces básicamente sirven para decir qué métodos ha de tener una clase, pero no para decir cómo han de comportarse esos métodos, ya que eso lo deciden las propias clases que lo implementan.

Un saludo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
2
Comentar
sin imagen de perfil

Necesito ayuda con este problema

Publicado por Martin (3 intervenciones) el 06/07/2021 04:51:51
Hola Kabuto!!
Buenas Noches!
Te quería agradecer porque me salvaste con este ejercicio, tu explicación fue muy clara y creeeeo que logré entender el tema.
Para asegurarme de esto intenté hacer un Diagrama de Clases, y me salió lo siguiente;
terraza
No tengo idea si está bien, te quería preguntar por eso y otras dudas.
Hay alguna variante para hacer CompositeFunction? Es decir, se puede hacer de otra manera? (No es porque no haya entendido, solo por curiosidad)
Un saludo!!
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
Imágen de perfil de Kabuto
Val: 3.428
Oro
Ha mantenido su posición en Java (en relación al último mes)
Gráfica de Java

Necesito ayuda con este problema

Publicado por Kabuto (1381 intervenciones) el 07/07/2021 00:56:28
El diagrama puede estar bien, según el significado que le quieras dar a esas flechas.
En un diagrama UML, se usan distintos tipos de flechas para representar el tipo de relación que hay entre las clases.

En este caso, se trataría de una relación de "agregación". Esta relación se representa con una flecha que nace de las clases que se "agregan" y acaba con un rombo blanco en la clase a la que son agregadas.(clases "contenedora")
Es decir, las clases Lineal, Cuadrática, Seno... y cualquier otra que implemente la interface Function, se agregan a la clase Composite.
Eso incluye también a Composite, es decir, podríamos tener una clase Composite, compuesta por otras dos Composite. Porque Composite también implementa Function igual que las otras clases.

La relación es de "agregación" porque Composite, NO puede existir sin esas clases Function. Pero esas clases Function, pueden existir sin Composite.

En cambio, si las clases "agregadas" también necesitasen de la clase "contenedora" para existir, no sería una relación de "agregación".
Sería una relación de "composición", que suele representarse con una flecha que acaba con un rombo pintado de negro.
Por ejemplo, una Empresa se compone de Departamentos (Mantenimiento, Recursos Humanos, Contabilidad, Formación, Enfermería...).
No puede haber Empresa sin Departamentos. Pero tampoco pueden haber Departamentos si no hay Empresa.



Sobre tu segunda pregunta.
Sí se podría hacer lo mismo que hemos hecho de otra manera.
Y esa otra manera sería haber declarado Function como clase "abstracta" y no como una interface.
El método evaluate() también lo incluiría como abstracto.

El resto de clases, serían hijas (heredarían) de Function y, igual que antes, heredarían el método evaluate() y tendrían que definir su funcionamiento.

Esto nos daría exactamente el mismo resultado que hemos obtenido usando Function como interfaz.

La diferencia entre "heredar de una clase madre" e "implementar una interfaz", es que la herencia está limitada a una sola clase.
Es decir, las clases Lineal, Seno, etc... solo podrán heredar de Function, porque solo pueden tener una "madre".

En cambio, pueden implementar todas las interfaces que queramos. Implementar una interfaz es casi como poder tener muchas "madres".

Y digo "casi", porque por supuesto, las interfaces tienen una desventaja.
Y esta desventaja, como ya comenté en mi mensaje anterior, es que las interfaces "no tienen código". Sirven para declarar las funcionalidades que ha de tener la clase que implemente esa interfaz, pero no para definir el código de esas funciones.

En cambio, al heredar de una clase, se adquieren métodos cuyo código ya está escrito, y ya elegimos si queremos sobreescribirlos o no.

Por ejemplo si yo declaro una clase llamada MiCadena que hereda de String

1
2
3
public class MiCadena extends String {
 
}

Automáticamente, esta clase MiCadena ya tiene todas las funciones de un String: el método split(), el charAt(), el subString(), el indexOf(), el compare(), el contains(), el startsWith(), el toUpperCase(), etc....
Todos ya con un código escrito y listos para funcionar sin que tengamos que hacer nada de nada.

En cambio, si String fuera una interfaz, en lugar de una clase..., a todos esos métodos tendríamos que escribirles el código para que funcionasen.

Así que "heredar una clase" e "implementar una interfaz", se parecen. Pero son cosas distintas y nosotros mismos hemos de decidir en que caso nos interesa una cosa o la otra.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
1
Comentar