Java, упрощающий повторяющийся код, включающий логическое сравнение

#java #simplify

Вопрос:

Я пытаюсь найти способ уменьшить длину и упростить следующие повторяющиеся методы :

 boolean circleFlag, squareFlag, diamondFlag;

public void shapeButtonPressed(String shapeType) {

    if (shapeType.equals("Circle")) {
        circlePressed();
    } else if (shapeType.equals("Square")) {
        squarePressed();
    } else if (shapeType.equals("Diamond")) {
        diamondPressed();   
    }

}

public void circlePressed() {
    if(!circleFlag){
        //set only circleFlag true and the rest false.
        circleFlag = true;
        squareFlag = false;
        diamondFlag = false;

        //(... some code)
    } else {
        //set all flags false.
        circleFlag = false; 
        diamondFlag = false
        squareFlag = false;

        //(... some different code)
    }

}
public void squarePressed() {
    if(!squareFlag){
        //set only squareFlag true and the rest false.
        squareFlag = true;
        circleFlag = false;
        diamondFlag = false;

        //(... some code)
    } else {
        //set all flags false.
        circleFlag = false; 
        diamondFlag = false
        squareFlag = false;

        //(... some different code)
    }

}
public void diamondPressed() {
    if(!diamondFlag){
        //set only diamondFlag true and the rest false.
        diamondFlag = true;
        squareFlag = false;
        circleFlag = false;

        //(... some code)
    } else {
        //set all flags false.
        circleFlag = false; 
        diamondFlag = false
        squareFlag = false;

        //(... some different code)
    }
}
 

Вещи, которые я пробовал

Я попытался установить все свои значения в Boolean тип, установить их в a ArrayList<Boolean> и изменить shapePressed(String shapeType) метод на

 public void shapePressed(String shapeType) {

    Boolean currFlag = false;

    if (shapeType.equals("Circle")) {
        currFlag = circleFlag;
    } else if (shapeType.equals("Square")) {
        currFlag = squareFlag;
    } else if (shapeType.equals("Diamond")) {
        currFlag = diamondFlag;
    }

    if (!currFlag){
        for (Boolean flag : shapeFlag) flag = ( flag == currFlag ) ? true : false; 

        //(...)
    } else {
        for (Boolean flag : shapeFlag) flag = false;

        //(...)
    }
}
 

но строка ( flag == currFlag ) сравнивает логические значения как значения, а не как отдельные объекты. Так что мой currFlag метод бессмыслен в этом вышеописанном методе.

Затем я подумал об использовании HashMap<String ,Boolean> , но всякий раз, когда я сравниваю значения, заданные ключом (тип формы строки из параметра метода), я сталкиваюсь с той же проблемой, что и выше.

Как можно упростить этот код ?

Комментарии:

1. Вместо if (circleFlag) { ...} этого вы могли бы просто сделать circleFlag = !circleFlag; , а затем установить другие флаги в значение false.

2. Вероятно, существует множество способов сделать это, но я мог бы предложить использовать перечисление для представления ваших различных фигур и сохранить currFlag тип перечисления, который представляет текущую фигуру.

Ответ №1:

Когда заданная фигура активирована, вы просто переворачиваете этот флаг. Затем для других флагов устанавливается значение false.

Таким образом, тривиально, вы могли бы упростить свою circlePressed() логику до:

 public void circlePressed() {
    circleFlag = !circleFlag;
    squareFlag = false;
    diamondFlag = false;

}
 

Конечно, все еще много повторений. Вы могли бы переработать это дальше в перечисление и отслеживать состояние там.

 public enum Flag {
  CIRCLE( false ),
  SQUARE( false ),
  DIAMOND( false ); // default state is false for all

  private boolean state;
  private Flag(boolean state) {
    this.state = state;
  }

  public void flipState() {
    this.state = !this.state;
  }

  public void setState(boolean state) {
    this.state = state;
  }
}

// notice this method takes the Flag not a string
public void shapeButtonPressed(Flag selected) {

    // iterate through all the flags ...
    for( Flag flag : Flag.values() ) {
        if (flag == selected) {
            // invert the "pressed" flag state
            flag.flipState();
        } else {
            // ... and set the rest to false
            flag.setState(false);
        }
    }
}
 

Встроенный values метод перечислений возвращает список всех определенных перечислений, поэтому мы можем просто перебирать их.

Я признаю, что это немного хитроумно, так как на самом деле это не то, для чего предназначены перечисления, но это немного упрощает вашу логику.

Комментарии:

1. Я не совсем понимаю, зачем для начала отслеживать состояние по трем переменным, если только одна из них может быть истинной одновременно.

2. activeState Переменная может иметь смысл, но, к сожалению, мы мало знаем о вариантах использования, связанных с фрагментом кода.

Ответ №2:

Вы могли бы использовать перечисление.

 public enum Shape {
  CIRCLE, SQUARE, DIAMOND
}
 

Затем используйте это в своем коде следующим образом;

 Shape shape;

public void shapeButtonPressed(Shape selectedShape) {
    shape = selectedShape;
}
 

Если вы не можете изменить сигнатуру метода shapeButtonPressed , и он должен принимать строку, вы все равно можете это сделать

 Shape shape;

public void shapeButtonPressed(String shapeType) {
    if (shapeType.equals("Circle")) {
        shape = Shape.CIRCLE;
    } else if (shapeType.equals("Square")) {
        shape = Shape.SQUARE;
    } else if (shapeType.equals("Diamond")) {
        shape = Shape.DIAMOND;
    }

}
 

Комментарии:

1. Можно написать более разумный выбор правильного перечисления вместо цепочки if . Вы можете выбрать, например, на основе его названия. Вы также можете ввести статическую функцию в класс перечисления, которая выполняет сопоставление за вас. Это всего лишь доказательство концепции, а не самый сложный способ ее решения.

2. Если оператору необходимо иметь возможность «отменить выбор» фигуры при нажатии одной и той же кнопки фигуры, код также может быть тривиально изменен, чтобы установить shape = null , когда shape == shapeType не представлять выбор.

3. @Йоханнес. Спасибо, я забыл о перечислениях. Но позже в моем коде, в других методах, у меня иногда бывает что-то вроде if(circleFlag || squareFlag || diamondFlag) { //(...) } . Означает ли это, что я не могу использовать предоставленный вами метод ?

4. Вам просто нужно изменить код. Вы можете сделать либо if (shape == Shape.SQUARE || shape == Shape.DIAMOND || shape == Shape.CIRCLE) {...} то, либо другое, если этих трех фигур больше нет, просто используйте if (shape != null) {...}

5. @JohannesH О да, это потрясающе ! Большое вам спасибо за вашу помощь :).

Ответ №3:

В качестве альтернативы моему подходу с перечислениями выше (который я настоятельно рекомендую по сравнению с этим), вы можете создать более «C-образное» решение, используя битовую маску вместо логических флагов.

Битовая маска-это, по сути, числовое (или двоичное, если на то пошло) значение, каждый бит которого представляет логическое значение.

 int shapeFlags;

public void shapeButtonPressed(String shapeType) {
    if (shapeType.equals("Circle")) {
        shapeFlags = 1;
    } else if (shapeType.equals("Square")) {
        shapeFlags = 2;
    } else if (shapeType.equals("Diamond")) {
        shapeFlags = 4;
    }
}
 

Это по-прежнему оставляет вам возможность установить более одной фигуры true , при этом вы можете переопределить все флаги за одну операцию.

Сопоставления числовых значений с фигурами будут выглядеть следующим образом:

 0 : no shape
1 : circle
2 : square
3 : circle amp; square
4 : diamond
5 : diamond amp; circle
6 : diamond amp; square
7 : all three