java - switch - Cómo reducir las declaraciones if
switch en java netbeans ejemplos (9)
El siguiente programa funciona según sea necesario, pero ¿cómo puedo reducir la cantidad de sentencias if? Me han dicho que si su función contiene 2 o más declaraciones if entonces lo está haciendo mal. ¿Alguna sugerencia? Intenté usar instrucciones de conmutación, pero eso no funcionó porque el caso no puede ser booleano.
for(int i = 1; i < 100; i++)
{
if(i % 10 == 3)
{
System.out.println("Fizz" + "(" + i + ") 3%10");
}
if(i / 10 == 3)
{
System.out.println("Fizz" + "(" + i + ") 3/10");
}
if(i % 10 == 5)
{
System.out.println("Buzz" + "(" + i + ") 5%10");
}
if(i / 10 == 5)
{
System.out.println("Fizz" + "(" + i + ") 5/10");
}
if(i / 10 == 7)
{
System.out.println("Fizz" + "(" + i + ") 7/10");
}
if(i%10 == 7)
{
System.out.println("Woof" + "(" + i + ") 7%10");
}
if(i % 3 == 0)
{
System.out.println("Fizz" + "(" + i + ") 3%==0");
}
if(i % 5 == 0)
{
System.out.println("Buzz" + "(" + i + ")5%==0");
}
if(i % 7 == 0)
{
System.out.println("Woof" + "(" + i + ")7%==0");
}
if( (i % 7 !=0 ) && (i % 3 !=0 ) && (i % 5 !=0 )
&& (i % 10 !=3) && (i % 10 !=5 ) && (i%10 !=7 ) )
System.out.println(i);
}
¿Qué tal crear un método para los casos?
public void printIfMod(int value, int mod){
if (value % 10 == mod)
System.out.println(...);
}
public void printIfDiv(int value, int div){
if (value / 10 == div)
System.out.println(...);
}
Entonces, en lugar de un montón de if
tienes un conjunto de llamadas, los dos métodos. Incluso puede crear un único método que llame a ambos de los anteriores.
public void printIf(int value, int div){
printIfMod(value, div);
printIfDiv(value, div);
}
for(int i = 1; i < 100; i++) {
printIf(i, 3);
printIf(i, 5);
....
}
En el código anterior, el número de ifs
es menos problemático para mí que la cantidad de código repetido.
Aquí hay una ligera mejora usando dos declaraciones de cambio
switch(i / 10){
case 3: // do something
break;
case 5: // do something else
break;
case 7: // do something else
break;
}
switch(i % 10){
case 3: // do something
break;
case 5: // do something else
break;
case 7: // do something else
break;
}
Desafortunadamente necesitarás una declaración de cambio por divisor.
Alternativamente, puede adoptar OOP y proponer una abstracción como esta:
public abstract class Processor {
private final int divisor;
private final int result;
private final boolean useDiv; // if true, use /, else use %
public Processor(int divisor, int result, boolean useDiv) {
this.divisor = divisor;
this.result = result;
this.useDiv = useDiv;
}
public final void process(int i){
if (
(useDiv && i / divisor == result)
|| (!useDiv && i % divisor == result)
){
doProcess(i);
}
}
protected abstract void doProcess(int i);
}
Uso de muestra:
public static void main(String[] args) {
List<Processor> processors = new ArrayList<>();
processors.add(new Processor(10, 3, false) {
@Override
protected void doProcess(int i) {
System.out.println("Fizz" + "(" + i + ") 3%10");
}
});
// add more processors here
for(int i = 1; i < 100; i++){
for (Processor processor : processors) {
processor.process(i);
}
}
}
Empecé a escribir una respuesta que involucraba código, pero mucha, mucha gente me golpeó. Lo único que diría que no se ha mencionado aún es que esta métrica de código particular a la que se refiere se llama complejidad ciclomática y no es una cosa horrendamente mala.
En resumen, se refiere a la cantidad de caminos diferentes que un método podría tomar cuando se ejecuta, y si bien es bastante alto en el código recortado que publicaste, y hay muchos buenos consejos / soluciones para reducirlo que se han sugerido, personalmente Yo diría que incluso en su forma actual, el código es muy legible, lo cual es una ventaja. Se puede reducir una cantidad justa y seguir siendo legible, pero mi punto es que las métricas como esa no lo son todo, y a veces puede ser más simple tener muchas declaraciones if
porque es más legible, y la legibilidad reduce la probabilidad de cometer errores. y hace que la depuración sea mucho más fácil
Ah, y reemplazaría esta última sección:
if( (i % 7 !=0 ) && (i % 3 !=0 ) && (i % 5 !=0 )
&& (i % 10 !=3) && (i % 10 !=5 ) && (i%10 !=7 ) )
System.out.println(i);
Al usar un indicador booleano como replaced = true
cada vez que se llama a cualquiera de las declaraciones de reemplazo, la instrucción anterior colapsa a:
if (!replaced)
System.out.println(i);
En términos generales, es cierto que el código que tiene muchas declaraciones if
parece sospechoso. Sospechoso no significa necesariamente incorrecto. Si la declaración del problema tiene condiciones disjuntas para verificar (es decir, no puede agruparlas), entonces tiene que hacerlas de forma independiente como lo hace.
En su caso, debe verificar la divisibilidad sin poder inferir una de la otra (es decir, si x es divisible por 7, no significa que también sea divisible por 5, etc.). Todos los números que estás usando han sido escogidos a propósito, así que es por eso que te estás metiendo en esto.
Si, por ejemplo, hubieran dicho, verifique la divisibilidad en 2, 3 y 6. Luego, primero podría verificar 6 porque entonces también podría implicar divisibilidad entre 2 y 3. O viceversa, verificar con 2 y 3 e implicar que también es divisible por 6. Si todos los números son primos, entonces no se puede insinuar. Entonces tu código tiene que verificar todo individualmente.
Un efecto secundario positivo es que hace que su intención sea fácil de leer en su código (porque es todo explícito).
Mis dos centavos en esto ...
Los mensajes son una buena opción aquí. Le permiten encapsular la funcionalidad en una ubicación en lugar de distribuirla a lo largo de su control de flujo.
public class Test {
public enum FizzBuzz {
Fizz {
@Override
String doIt(int n) {
return (n % 10) == 3 ? "3%10"
: (n / 10) == 3 ? "3/10"
: (n / 10) == 5 ? "5/10"
: (n / 10) == 7 ? "7/10"
: (n % 3) == 0 ? "3%==0"
: null;
}
},
Buzz {
@Override
String doIt(int n) {
return (n % 10) == 5 ? "5%10"
: (n % 5) == 0 ? "5%==0"
: (n / 10) == 3 ? "3/10"
: (n / 10) == 5 ? "5/10"
: (n / 10) == 7 ? "7/10"
: null;
}
},
Woof {
@Override
String doIt(int n) {
return (n % 10) == 7 ? "7%10"
: (n % 7) == 0 ? "7%==0"
: null;
}
};
// Returns a String if this one is appropriate for this n.
abstract String doIt(int n);
}
public void test() {
// Duplicates the posters output.
for (int i = 1; i < 100; i++) {
boolean doneIt = false;
for (FizzBuzz fb : FizzBuzz.values()) {
String s = fb.doIt(i);
if (s != null) {
System.out.println(fb + "(" + i + ") " + s);
doneIt = true;
}
}
if (!doneIt) {
System.out.println(i);
}
}
// Implements the game.
for (int i = 1; i < 100; i++) {
boolean doneIt = false;
for (FizzBuzz fb : FizzBuzz.values()) {
String s = fb.doIt(i);
if (s != null) {
if ( doneIt ) {
System.out.print("-");
}
System.out.print(fb);
doneIt = true;
}
}
if (!doneIt) {
System.out.print(i);
}
System.out.println();
}
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Puedes crear múltiples cambios:
switch (i/10) {
case 3:
System.out.println("Fizz" + "(" + i + ") 3/10");
break;
case 5:
System.out.println("Fizz" + "(" + i + ") 5/10");
break;
case 7:
System.out.println("Fizz" + "(" + i + ") 7/10");
break;
default:
break;
}
switch (i%10) {
case 3:
System.out.println("Fizz" + "(" + i + ") 3%10");
break;
case 5:
System.out.println("Buzz" + "(" + i + ") 5%10");
break;
case 7:
System.out.println("Woof" + "(" + i + ") 7%10");
break;
default:
break;
}
El otro caso todavía tiene que usar la instrucción if.
Oracle agregó la declaración de cambio que utilizó String
en Java 7. Tal vez la declaración de cambio booleano vendrá después.
Tu código es repetitivo Refactorizarlo usando bucles para su:
for (int i = 1; i < 100; i++) {
boolean found = false; // used to avoid the lengthy test for "nothing found"
for (int j = 3; j <= 7; j += 2) { // loop 3, 5, 7
if (i % 10 == j) {
System.out.println("Fizz" + "(" + i + ") "+j+"%10");
found = true;
}
if (i / 10 == j) {
System.out.println("Fizz" + "(" + i + ") "+j+"/10");
found = true;
}
if (i % j == 0) {
System.out.println("Fizz" + "(" + i + ") "+j+"%==0");
found = true;
}
}
if (!found) {
System.out.println(i);
}
}
Yo diría que estás haciendo una pregunta incorrecta. La pregunta que creo que deberían hacerse es: "¿Cómo puedo reescribir este código para que sea más fácil de entender para un humano?"
El credo "eliminar declaraciones if" es una idea general para lograr esto, pero depende en gran medida del contexto.
La triste realidad es que muchas de las respuestas ofuscan este algoritmo simple con el pretexto de "hacerlo más simple". Nunca introduzca un objeto para eliminar un par de declaraciones if. En mi trabajo, la mayoría del código es mantenido por personas que entienden mucho menos sobre la arquitectura, las matemáticas y el código que el autor original, por lo que introducen construcciones adicionales y complejidad para reducir el código de 50 líneas físicas a 30 líneas físicas, pero lo convierten en 4 veces más difícil de entender no es una victoria.
public class Test
{
public static void main(String[] args)
{
final int THREE = 3;
final int FIVE = 5;
final int SEVEN=7;
final int ZERO = 0;
for (int i = 1; i < 100; i++)
{
modOperation("Fizz", i, THREE);
divideOperation("Fizz", i, THREE);
modOperation("Fizz", i, FIVE);
divideOperation("Buzz", i, FIVE);
modOperation("Woof", i, SEVEN);
divideOperation("Fizz", i, SEVEN);
modOperation("Fizz", i, ZERO);
divideOperation("Fizz", i, ZERO);
}
}
private static void divideOperation(String sound, int i, int j)
{
if (i / 10 == j) // you can add/expand one more parameter for 10 and later on 3 in this example.
{
System.out.println(sound + "(" + i + ") "+j+"/10");
}
}
private static void modOperation(String sound, int i, int j)
{
if (i % 10 == j)
{
System.out.println(sound + "(" + i + ") "+j+"%10");
}
}
}
Entonces ahora tienes menos if