private static void Solucion() { for (int i = 0; i < 100; i++) { if (i % 3 == 0 && i % 5 == 0) System.out.println("fizzbuzz"); else if (i % 3 == 0) System.out.println("fizz" + i); else if (i % 5 == 0) System.out.println("buzz" + i); else System.out.println(i); } }Como vemos, no cumple. Sí, solo muestra uno de los textos y sí, los muestra correctamente pero... No son los números del 1 al 100. Sorprendentemente, ninguno uso números del 1 al 100. Raro ¿no?
private static void Solucion() { for (int i = 0; i < 100; i++) { if (multiplo3(i) && multiplo5(i)) System.out.println("algoquenomeacuerdo"); if (multiplo3(i)) System.out.println("fizz" + i); if (multiplo5(i)) System.out.println("buzz" + i); System.out.println(i); } } private static boolean multiplo5(int i) { return i % 5 == 0; } private static boolean multiplo3(int i) { return i % 3 == 0; }Esta fué otra solución. Otra vez del 0 al 99 y en este caso para el 15 muestra 4 textos. Leyendo esto, ¿A alguno se le ocurre que fuera tan difícil el agoritmo?. No el algoritmo no es tan difícil pero los nervios pueden jugar una mala pasada. Pero estoy dando mucho rodeo, vamos al grano.
La solución esperada no es para nada compleja y está escrita en Java, pero se les dio opción de usar lo que quisieran, tenían Visual Studio y NetBeans y alguna otra cosilla para elegir como IDE. Es esta:
private static void Solucion() { for (int i = 1; i <= 100; i++) { if (i % 3 == 0 && i % 5 == 0) System.out.println("fizzbuzz" + i); else if (i % 3 == 0) System.out.println("fizz" + i); else if (i % 5 == 0) System.out.println("buzz" + i); else System.out.println(i); } }Ahora bien, ¿Este algoritmo es escalable? La respuesta es no. Si yo quiero añadir nuevos requisitos o quiero un algoritmo que ejecute unos ifs u otros en función de unos checks que se le muestren al usuario, no puedo usar este tipo de algoritmo o tendré un montón de condiciones complejas de mantener.
Para solucionarlo, podemos crear una clase que contenga dos objetos, uno para comprobar si el número es válido para ejecutar el código (parte del if) y otro que contenga el algoritmo que se ejecute si el código es válido. Para ello necesitamos dos interface, una por algoritmo y con un único método, así son interface funcionales y puedo usar expresiones lambda con ellos. Esta es la implementación:
interface CompruebaValido { public boolean esValido(int i); } interface Procesador { public void procesa(int i); } class CompruebaYProcesa { private CompruebaValido compruebaValido; private Procesador procesador; public CompruebaYProcesa(CompruebaValido compruebaValido, Procesador procesador) { this.compruebaValido = compruebaValido; this.procesador = procesador; } public boolean esValido(int i) { return compruebaValido.esValido(i); } public void procesa(int i) { procesador.procesa(i); } }Como se puede ver, la clase CompruebaYProcesa delega en sus objetos para crear sus dos métodos. Y la implementación del algoritmo queda así:
ArrayList<CompruebaYProcesa> comPro = new ArrayList<>(); // Uso lambdas en vez de clases anónimas, el código queda más claro comPro.add(new CompruebaYProcesa((i) -> i % 5 == 0 && i % 3 == 0, (i) -> System.out.println("no se que " + i))); comPro.add(new CompruebaYProcesa((i) -> i % 3 == 0, (i) -> System.out.println("FIZZ " + i))); comPro.add(new CompruebaYProcesa((i) -> i % 5 == 0, (i) -> System.out.println("BUZZ " + i))); // Muestro para comprobarlo System.out.println(comPro); for (int i = 0; i < 100; i++) { boolean haSidoValido = false; // Recorro los objetos para comprobar y si has sido válido lo guardo en el boolean y hago break // para que no salgan cosas repetidas for (CompruebaYProcesa cp : comPro) { if (cp.esValido(i)) { cp.procesa(i); haSidoValido = true; break; } } // Si no ha sido válido lo muestro sin texto if (!haSidoValido) System.out.println(i); }Pero esto tiene un problema, que se van a ejecutar en el orden en que las añada, si por ejemplo, la condición del 3 y el 5 la añadiera al final, esta no se ejecutaría para el 15, si no que se ejecutaría la primera, por ejemplo, la del 3, pues cumple la condición de ser múltiplo de 3. Para solucionar este problema, cambiamos un poco la clase CompruebaYProcesa para añadirle un int con la prioridad y antes de ejecutarlo ordenamos el ArrayList:
class CompruebaProcesaYPrioriza { private final CompruebaValido compruebaValido; private final Procesador procesador; public final int prioridad; public CompruebaProcesaYPrioriza(CompruebaValido compruebaValido, Procesador procesador, int prioridad) { this.compruebaValido = compruebaValido; this.procesador = procesador; this.prioridad = prioridad; } public boolean esValido(int i) { return compruebaValido.esValido(i); } public void procesa(int i) { procesador.procesa(i); } @Override public String toString() { return "CompruebaProcesaYPrioriza{" + "prioridad=" + prioridad + '}'; } }Y el algoritmo queda así:
comPro = new ArrayList<>(); comPro.add(new CompruebaProcesaYPrioriza((i) -> i % 3 == 0, (i) -> System.out.println("FIZZ " + i), 50)); comPro.add(new CompruebaProcesaYPrioriza((i) -> i % 5 == 0, (i) -> System.out.println("BUZZ " + i), 10)); comPro.add(new CompruebaProcesaYPrioriza((i) -> i % 5 == 0 && i % 3 == 0, (i) -> System.out.println("no se que " + i), 100)); // Borro uno para ver resultado aleatorios comPro.remove(new Random().nextInt(comPro.size())); // Ordeno por prioridad descendente (de mayor a menor) Collections.sort(comPro, (o1, o2) -> o1.prioridad < o2.prioridad ? 1 : -1); // Muestro para comprobarlo System.out.println(comPro); for (int i = 0; i < 100; i++) { boolean haSidoValido = false; // Recorro los objetos para comprobar y si has sido válido lo guardo en el boolean y hago break // para que no salgan cosas repetidas for (CompruebaProcesaYPrioriza cp : comPro) { if (cp.esValido(i)) { cp.procesa(i); haSidoValido = true; break; } } /* Si no ha sido válido lo muestro sin texto */ if (!haSidoValido) System.out.println(i); }De esta manera, se le pueden añadir condiciones a partir de selecciones del usuario y ejecutarlas con prioridad, para que, por ejemplo si elige las condiciones del 3 y del 3 y el 5, se ejecute primero la que tenga más prioridad, da igual el orden.
La anterior entrada que hablaba del Patrón estrategia es: http://morethansimplycode.blogspot.com.es/2015/02/enums.html
No hay comentarios:
Publicar un comentario