Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any techniques to split a method with a flag argument?

I have a method with a flag argument. I think that passing a boolean to a method is a bad practice (complicates the signature, violates the "each method does one thing" principle). I think splitting the method into two different methods is better. But if I do that, the two methods would be very similar (code duplication).

I wonder if there are some general techniques for splitting methods with a flag argument into two separate methods.

Here's the code of my method (Java):

int calculateNumOfLiveOrDeadNeighbors(Cell c, int gen, boolean countLiveOnes) {
   int x = c.getX();
   int y = c.getY();
   CellState state;
   int aliveCounter = 0;
   int deadCounter = 0;
   for (int i = x - 1; i <= x + 1; i++) {
      for (int j = y - 1; j <= y + 1; j++) {
         if (i == x && j == y)
            continue;
         state = getCell(i, j).getCellState(gen);
         if (state == CellState.LIVE || state == CellState.SICK){
            aliveCounter++;
         }
         if(state == CellState.DEAD || state == CellState.DEAD4GOOD){
            deadCounter++;
         }
      }
   }
   if(countLiveOnes){
      return aliveCounter;
   }
   return deadCounter;
}
like image 500
snakile Avatar asked Nov 24 '10 11:11

snakile


People also ask

What is a flag argument?

A flag argument is a kind of function argument that tells the function to carry out a different operation depending on its value.

Are flags a code smell?

In most major code bases, it's common to have objects with lots of Boolean flags indicating what state they're in.

How do you reduce the number of arguments in Java?

You can break the method into smaller methods, then call the smaller methods from the parent method. Sometimes you can then call the extracted method directly and remove this call from this original method. This can lead to decreasing the number of method parameters.


2 Answers

If you don't like the boolean on your signature, you could add two different methods without it, refactoring to private the main one:

int calculateNumOfLiveNeighbors(Cell c, int gen) {
  return calculateNumOfLiveOrDeadNeighbors(c, gen, true);
}
int calculateNumOfDeadNeighbors(Cell c, int gen) {
  return calculateNumOfLiveOrDeadNeighbors(c, gen, false);
}

OR

you could code a Result Class or int array as output parameter for storing both the results; this would let you get rid of the annoying boolean parameter.

like image 73
systempuntoout Avatar answered Sep 20 '22 17:09

systempuntoout


I guess it depends on every single case.

In this example you have two choices, in my opinion.

Say you want to split the call calculateNumOfLiveOrDeadNeighbors()

in two:

calculateNumOfLiveNeighbors() 

and

calculateNumOfDeadNeighbors()

You can use Template Method to move the loop to another method. You can use it to count dead / alive cells in the two methods.

private int countCells(Cell c, int gen, Filter filter)
{
    int x = c.getX();
    int y = c.getY();
    CellState state;
    int counter = 0;
    for (int i = x - 1; i <= x + 1; i++) 
    {
        for (int j = y - 1; j <= y + 1; j++) 
        {
            if (i == x && j == y)
                continue;
            state = getCell(i, j).getCellState(gen);
            if (filter.countMeIn(state))
            {
                counter++;
            }
        }
    }
    return counter;
 }

 private interface Filter
 {
      boolean countMeIn(State state);
 }

 public int calculateNumOfDeadNeighbors(Cell c, int gen)
 {
     return countCells(c, gen, new Filter()
                       { 
                           public boolean countMeIn(CellState  state)
                           {
                              return (state == CellState.DEAD || state == CellState.DEAD4GOOD);
                           }
                        });
  }

 public int calculateNumOfLiveNeighbors(Cell c, int gen)
 {
     return countCells(c, gen, new Filter()
                       { 
                           public boolean countMeIn(CellState  state)
                           {
                              return (state == CellState.LIVE || state == CellState.SICK);
                           }
                        });
  }

It's cumbersome, maybe not even worth the pain. You can, alternatively, use a monad to store the results of your statistics calculation and then use getDeadCounter() or getLiveCounter() on the monad, as many suggested already.

like image 23
Manrico Corazzi Avatar answered Sep 22 '22 17:09

Manrico Corazzi