1
\$\begingroup\$

I believe this set of code is very repetitive in a way where every method does almost the same task but it is repeated multiple times. This is what the code looks like and its main purpose is to display the intended design on the simulator:

private void configStart() {
 button1 = new JButton("start");
 button1.setBackground(Color.lightGray);
 button1.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 if(vehicle == null) {
 int selectedIndex = combobox.getSelectedIndex();
 String vehicleName = vehicles[selectedIndex];
 initialiseVehicle(vehicleName);
 speedlabel.setText(vehicle.printSpeed());
 }
 if(simulationPane !=null) {
 frame.remove(simulationPane);
 }
 accelerate = false;
 decelerate = false;
 cruise = false;
 stop = false;
 button1.setBackground(Color.GREEN);
 button2.setBackground(Color.lightGray);
 button3.setBackground(Color.lightGray);
 button4.setBackground(Color.lightGray);
 button5.setBackground(Color.lightGray);
 simulationPane = new Simulator();
 frame.add(simulationPane,BorderLayout.CENTER);
 frame.revalidate();
 frame.repaint();
 }
 });
}
private void configAccelerate() {
 button2 = new JButton("accelerate");
 button2.setBackground(Color.lightGray);
 button2.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 accelerate = true;
 decelerate = false;
 cruise = false;
 stop = false;
 button1.setBackground(Color.lightGray);
 button2.setBackground(Color.green);
 button3.setBackground(Color.lightGray);
 button4.setBackground(Color.lightGray);
 button5.setBackground(Color.lightGray);
 Thread thread = new Thread(){
 public void run(){
 try {
 while(accelerate) {
 Thread.sleep(2 * 1000);
 if(currentvelocity<=maximumvelocity) {
 currentvelocity = currentvelocity +1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
 } 
 }
 }
 catch (InterruptedException e) {
 e.printStackTrace();
 } 
 }
 };
 thread.start();
 } 
 });
}
private void configCruise() {
 button3 = new JButton("cruise");
 button3.setBackground(Color.lightGray);
 button3.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 accelerate = false;
 decelerate = false;
 cruise = true;
 stop = false;
 button1.setBackground(Color.lightGray);
 button2.setBackground(Color.lightGray);
 button3.setBackground(Color.green);
 button4.setBackground(Color.lightGray);
 button5.setBackground(Color.lightGray);
 } 
 });
}
private void configDecelerate() {
 button4 = new JButton("decelerate");
 button4.setBackground(Color.lightGray);
 button4.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 accelerate = false;
 decelerate = true;
 cruise = false;
 stop = false;
 button1.setBackground(Color.lightGray);
 button2.setBackground(Color.lightGray);
 button3.setBackground(Color.lightGray);
 button4.setBackground(Color.green);
 button5.setBackground(Color.lightGray);
 
 Thread thread = new Thread(){
 public void run(){
 try {
 while(decelerate) {
 Thread.sleep(2 * 1000);
 if(currentvelocity >1) {
 currentvelocity = currentvelocity -1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
 } 
 }
 }
 catch (InterruptedException e) {
 e.printStackTrace();
 } 
 }
 };
 thread.start();
 } 
 });
}
private void configStop() {
 button5 = new JButton("stop");
 button5.setBackground(Color.lightGray);
 button5.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 accelerate = false;
 decelerate = false;
 cruise = false;
 stop = true;
 button1.setBackground(Color.lightGray);
 button2.setBackground(Color.lightGray);
 button3.setBackground(Color.lightGray);
 button4.setBackground(Color.lightGray);
 button5.setBackground(Color.green);
 
 currentvelocity = 1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
 } 
 });
}

I would like to ask how it can be refactored to increase its modularity or reduce complexity. Thanks!

asked Feb 4, 2021 at 14:07
\$\endgroup\$
2

1 Answer 1

2
\$\begingroup\$

Since I cannot compile this code, there might be small mistakes in my code. I'm not writing Java for a long time. Hope it helps.

I see your buttons work as a state machine, so I created a enum called ButtonState to represent each state:

enum ButtonState {
 Start,
 Accelerate,
 Cruise,
 Decelerate,
 Stop;
}

I see you have a bunch of buttons named like button1, button2 ... etc. Don't name your variables with numbers. Instead name them with their associated actions, like startButton, accelerateButton etc. If you have a cluster of variables with same type, it indicates that you can store them in a container. I used array in this example. Since there is a button for each state we know the size of buttons:

JButton[] buttons = new JButton[ButtonState.values().length()];.

To initialize buttons we can iterate through states:

for (ButtonState state : ButtonState.values()) {
 JButton button = new JButton(state.name().toLowerCase()); // button name
 button.setBackground(Color.lightGray); // button color
 button.addActionListener(...); // button action
 buttons[state.ordinal()] = button;
}

You can get buttons with state ordinals, let's say you want to get cruise button, you can get it like buttons[ButtonState.Cruise.ordinal()].

I see you have a boolean variables for each state(except start). Let's assume you also have bool start to complete pattern. I see only one of them gets true and others get false each time. So instead of having booleans for each of state, we can have only one state that represents which state is true. In this case it is ButtonState state.

Before:

accelerate = false;
decelerate = true;
cruise = false;
stop = false;

After:

state = ButtonState.Decelerate;

Before:

if (decelerate)

After:

if (state == ButtonState.Decelerate)

Now we can get rid of most repetitive pattern in your code: Setting a state boolean and highlighting the corresponding button. Let's gather it in a function:

private void setState(ButtonState state) {
 this.buttons[this.state.ordinal()].setBackground(Color.lightGray); // fade highlighted button
 this.buttons[state.ordinal()].setBackground(Color.green); // highlight new state button
 this.state = state; // set state
}

Before:

accelerate = true;
decelerate = false;
cruise = false;
stop = false;
button1.setBackground(Color.lightGray);
button2.setBackground(Color.green);
button3.setBackground(Color.lightGray);
button4.setBackground(Color.lightGray);
button5.setBackground(Color.lightGray);

After:

this.setState(ButtonState.Accelerate);

Bonus: In a scenario where multiple states combined, like accelerating and cruising at the same time, enums with bit flags can be used.

Refactored Code

enum ButtonState {
 Start,
 Accelerate,
 Cruise,
 Decelerate,
 Stop;
}
private JButton[] buttons;
private ButtonState state;
private void setState(ButtonState state) {
 this.buttons[this.state.ordinal()].setBackground(Color.lightGray);
 this.buttons[state.ordinal()].setBackground(Color.green);
 this.state = state;
}
private void configButtons() {
 this.buttons = new JButton[ButtonState.values().length()];
 
 for (ButtonState state : ButtonState.values()) {
 JButton button = new JButton(state.name().toLowerCase());
 button.setBackground(Color.lightGray);
 
 switch(state) {
 case Start: 
 button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 start(e);
 }
 });
 break;
 
 case Accelerate: 
 button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 accelerate(e);
 }
 });
 break;
 
 case Cruise: return
 button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 cruise(e);
 }
 });
 break;
 
 case Decelerate:
 button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 decelerate(e);
 }
 });
 break;
 
 case Stop:
 button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 stop(e);
 }
 });
 break;
 }
 
 this.buttons[state.ordinal()] = button;
 }
}
private void start(ActionEvent e) {
 if(vehicle == null) {
 int selectedIndex = combobox.getSelectedIndex();
 String vehicleName = vehicles[selectedIndex];
 initialiseVehicle(vehicleName);
 speedlabel.setText(vehicle.printSpeed());
 }
 
 if(simulationPane !=null) {
 frame.remove(simulationPane);
 }
 
 this.setState(ButtonState.Start);
 simulationPane = new Simulator();
 frame.add(simulationPane,BorderLayout.CENTER);
 frame.revalidate();
 frame.repaint();
}
private void accelerate(ActionEvent e) {
 this.setState(ButtonState.Accelerate);
 Thread thread = new Thread(){
 public void run(){
 try {
 while(accelerate) {
 Thread.sleep(2 * 1000);
 if(currentvelocity<=maximumvelocity) {
 currentvelocity = currentvelocity +1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
 } 
 }
 }
 catch (InterruptedException e) {
 e.printStackTrace();
 } 
 }
 };
 thread.start();
}
private void cruise(ActionEvent e) {
 this.setState(ButtonState.Cruise);
}
private void decelerate(ActionEvent e) {
 this.setState(ButtonState.Decelerate);
 
 Thread thread = new Thread(){
 public void run(){
 try {
 while(decelerate) {
 Thread.sleep(2 * 1000);
 if(currentvelocity >1) {
 currentvelocity = currentvelocity -1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
 } 
 }
 }
 catch (InterruptedException e) {
 e.printStackTrace();
 } 
 }
 };
 thread.start();
}
private void stop(ActionEvent e) {
 this.setState(ButtonState.Stop);
 
 currentvelocity = 1;
 vehicle.setCurrentSpeed(currentvelocity);
 speedlabel.setText(vehicle.printSpeed());
 simulationPane.updateTimer();
}

Happy Coding;P

answered Feb 4, 2021 at 23:37
\$\endgroup\$
4
  • 1
    \$\begingroup\$ (Since I cannot compile this code more and more online compilation services are available (e.g., tutorialspoint.com).) \$\endgroup\$ Commented Feb 5, 2021 at 7:46
  • \$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer. \$\endgroup\$ Commented Feb 5, 2021 at 9:00
  • \$\begingroup\$ @greybeard I don't know how rest of class looks like, there are lots of members I have no idea about them, plus I have to import packages for JButton etc. More explanatory edit incoming. \$\endgroup\$ Commented Feb 5, 2021 at 10:01
  • \$\begingroup\$ @greybeard Have you even looked at the original code? I encourage you to actually try compiling it on Tutorialspoint. It just gives a continuous stream of errors. And even if those were fixed, it's a GUI. While it is barely possible to compile a GUI online (see repl.it ), Tutorialspoint can't. I realize that you may be busy. But if you don't have time to glance at the code, perhaps you shouldn't have time to post. As is, you have simply been gratuitously rude. \$\endgroup\$ Commented Feb 8, 2021 at 10:43

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.