java - programadores - prueba para programador junior
Prueba de programaciĆ³n Java para entrevista (9)
Algo artificial, parece más cercano al patrón decorador para mí. No estoy seguro de lo que diría durante una entrevista, pero aquí es cómo lo codificaría:
package math;
import java.util.ArrayList;
import java.util.List;
public class DecoratorMath
{
interface MathFunction
{
double calculate(double x);
}
public static void main(String[] args)
{
DecoratorMath decoratorMath = new DecoratorMath();
decoratorMath.go();
}
public void go()
{
Solver solver = new Solver();
decorate(solver);
List<Double> results = solver.solveAll(02);
for (Double d :results)
{
System.out.println(d);
}
}
public void decorate(Solver solver)
{
solver.addFunction(new MathFunction()
{
@Override
public double calculate(double x)
{
return Math.sqrt(x);
}
});
solver.addFunction(new MathFunction()
{
@Override
public double calculate(double x)
{
return 1d/x;
}
});
}
class Solver
{
private List<MathFunction> mathFunctions = new ArrayList<MathFunction>();
public void addFunction(MathFunction mathFunction)
{
mathFunctions.add(mathFunction);
}
public List<Double> solveAll(double x)
{
List<Double> result = new ArrayList<Double>();
for (MathFunction function : mathFunctions)
{
result.add(new Double(function.calculate(x)));
}
return result;
}
}
}
Aquí hay una prueba de programación utilizada en una entrevista de trabajo. Encuentro que tiene una perspectiva no-OO muy extraña y me pregunto por qué alguien se acercaría a un constructor desde esta perspectiva. Como programador de Java con mucha experiencia, inmediatamente cuestiono la capacidad del individuo que escribió este código y la extraña perspectiva de la pregunta.
Encuentro estas extrañas preguntas fuera de contexto en entrevistas inquietantes. Me encantaría recibir comentarios de otros programadores experimentados de OO Java.
Complete el constructor Solver para que una llamada a solveAll devuelva una lista con 2 valores, incluida la raíz cuadrada y el inverso del entero pasado como parámetro.
public interface MathFunction {
double calculate(double x);
}
public class Solver {
private List<MathFunction> functionList;
public Solver() {
//Complete here
}
public List<Double> solveAll(double x) {
List<Double> result = new ArrayList<Double>();
for (MathFunction function : this.functionList) {
result.add(new Double(function.calculate(x)));
}
return result;
}
}
Aquí está mi solución. Esta es una ilustración simple de una clase de fábrica .
public Solver() {
functionList = new ArrayList<MathFunction>();
MathFunction sqrt = new MathFunction() {
@Override
public double calculate(double x) {
return Math.sqrt(x);
}
};
functionList.add(sqrt);
MathFunction inverse = new MathFunction() {
@Override
public double calculate(double x) {
return 1.0D / x;
}
};
functionList.add(inverse);
}
Esta pregunta muestra dos cosas:
- Si el programador entiende términos matemáticos como inversa.
- Si el programador entiende que las instancias de las interfaces o las clases se pueden almacenar en una lista, e iterar más adelante.
Aunque estoy de acuerdo en que probablemente esta no sea la mejor manera, o la forma más correcta de hacerlo, tendría que asumir que el objetivo de este ejercicio es ver qué tan bien entiendes Herencia, Interfaces y quizás clases internas anónimas. Eso es lo único que puedo entender.
Creo que querían que agregaras dos elementos en la lista de funciones. Cada uno implementaría la interfaz MathFunction, uno para la raíz cuadrada y otro para la inversa. El problema reside en los detalles:
1- Usted tiene una función que devuelve 2 valores porque hace dos cosas diferentes, eso es malo
2- Si quieres tener esta clase "hazlo todo", m sería interesante recibir las funciones matemáticas como un parámetro para que puedas hacer cualquier tipo de funciones matemáticas, las funciones matemáticas serían parametrizables
En mi humilde opinión, de hecho es un enfoque extraño. El nombre Solver
es genérico, no debe implementar operaciones específicas por defecto. Sin embargo, tal vez eso fue parte de la entrevista? Primera parte: simplemente cumple la solicitud. Parte dos: di que es extraño hacerlo.
Yo diría que un enfoque mucho más agradable sería tener un addMathFunction(MathFunction mf)
. Y si lo desea, para crear subclases que amplíen la clase Solver
y añadan MathFunctions en su constructor.
Esto está probando sus patrones de diseño, utilizando el método más simple posible. Creo que esta podría ser la estrategia (o algún otro patrón de comportamiento). Ver estos:
http://en.wikipedia.org/wiki/Strategy_pattern
http://en.wikipedia.org/wiki/Behavioral_pattern
Si va a realizar una entrevista en Java, ¡debería ser capaz de identificar el patrón de diseño al que insinúan y que debería evitar que esté demasiado inquieto!
Para responder a la pregunta, cree dos clases que implementen MathFunction
según sea necesario, luego cree dos instancias y guárdelas en functionList
.
El punto aquí no es "¿puedes hacer cálculos de esta manera extraña?", Es "puedes identificar patrones de diseño".
Estoy de acuerdo en que es confuso y excesivamente ingenieril.
Pero creo que el código está razonablemente orientado a objetos. Es una instancia del patrón de estrategia. Al código que genera una lista de respuestas no le importa cómo se calculan las respuestas: las dos preocupaciones se separan y se puede aplicar una estrategia de cálculo diferente sin tener que tocar el código que genera la lista.
Para que la clase sea más útil, estas funciones se deben pasar desde el exterior (es decir, inyección de dependencia) en lugar de crear una instancia en el constructor.
Usted sabe la respuesta, supongo, pero por lo que vale ...
public Solver() {
functionList = new ArrayList<MathFunction>();
functionList.add(new MathFunction() {
@Override
public double calculate(double x) {
return 1d/x;
}
});
functionList.add(new MathFunction() {
@Override
public double calculate(double x) {
return Math.sqrt(x);
}
});
}
Hacer todo esto dentro del Constructor es solo una mala práctica. De todos modos mi solución todo en uno.
import java.util.*;
import java.math.*;
//sqrt / inverse
public class Solver{
private List<MathFunction> functionList;
public interface MathFunction{
double calculate(double x);
}
class X implements MathFunction {
public double calculate(double x) {
return Math.sqrt(x);
}
}
class Y implements MathFunction {
public double calculate(double y) {
return 1/y;
}
}
public Solver(){
//here
functionList = new ArrayList<MathFunction>();
MathFunction f = (MathFunction) new X();
functionList.add(f);
MathFunction f2 = (MathFunction) new Y();
functionList.add(f2);
}
public List<Double> solveAll(double x){
List<Double> result=new ArrayList<Double>();
for (MathFunction function : this.functionList){
result.add(new Double(function.calculate(x)));
}
return result;
}
public static void main(String... args) {
System.out.println("result="+new Solver().solveAll(123));
}
}
Ok, codifiqué la solución a mi propia pregunta. Mi instinto de que nada debería estar en el constructor parece ser correcto. El functionList no es estático, por lo que necesita una instancia para inicializarlo. Especifica entero así que redondeo a entero. La función inversa no es matemática avanzada de ninguna manera.
import java.util.ArrayList;
import java.util.List;
import java.lang.Math;
public class Solver {
private List<MathFunction> functionList = new ArrayList<MathFunction>();;
public Solver() {
// Complete here
}
public void initFunctionList() {
MathFunction functionSquareRoot = new MathFunction(){
@Override
public double calculate(double x) {
return (x<0 ? 0: Math.sqrt(x)); // maybe we need throw an exception here for negative numbers, but we''ll just set it to 0
}};
MathFunction functionInverse = new MathFunction(){
@Override
public double calculate(double x) {
return (x!=0.0 ? 1/x : 0);
}
};
functionList.add(functionSquareRoot);
functionList.add(functionInverse);
}
public List<Double> solveAll(double x) {
List<Double> result = new ArrayList<Double>();
for (MathFunction function : this.functionList) {
result.add(new Double(function.calculate(x)));
}
return result;
}
}
public interface MathFunction {
double calculate(double x);
}
public class TestSolver {
/**
* @param args
*/
public static void main(String[] args) {
Solver s = new Solver();
s.initFunctionList();
System.out.println(s.solveAll(16.0));
}
}
Me engaño a mí mismo el constructor puede ser
public Solver() {
// Complete here
MathFunction functionSquareRoot = new MathFunction(){
@Override
public double calculate(double x) {
return (x<0 ? 0: Math.sqrt(x)); // maybe we need throw an exception here for negative numbers, but we''ll just set it to 0
}};
MathFunction functionInverse = new MathFunction(){
@Override
public double calculate(double x) {
return (x!=0.0 ? 1/x : 0);
}
};
functionList.add(functionSquareRoot);
functionList.add(functionInverse);
}