Refactor Client Initialization: Streamline UI Flow

by Admin 51 views
Refactor Client Initialization: Streamline UI Flow

Hey guys! Today, we're diving deep into refactoring the client initialization flow in our application. Currently, the initial UI flow goes like this: ServerBrowser -> SignOn -> SplashWindow -> MainMenu. But the way it's implemented now isn't the most efficient. Instead of directly passing information between these components, we're using blocking modal dialogs. Let's break it down and see how we can make things smoother and more maintainable.

Understanding the Current Flow

Currently, the ServerBrowser and SignOn dialogs are set to block. This means that when they're visible, the application essentially waits for them to be completed before moving on. This approach, while straightforward, has some drawbacks:

  • Tight Coupling: The components are tightly coupled because they rely on global state or configuration to pass data.
  • Reduced Responsiveness: Blocking dialogs can make the application feel less responsive, as the user interface freezes until the dialog is closed.
  • Maintainability Issues: Changes to one component can have unintended consequences on others due to the tight coupling.

To address these issues, we're going to refactor the flow to use direct instantiation and data passing. This will make the code more modular, easier to test, and more responsive.

Refactoring the ServerBrowser

Our first task is to modify the ServerBrowser so that it no longer blocks when it's visible. Instead of setting the modal property to true, we'll set it to false. This allows the user to interact with other parts of the application while the ServerBrowser is open.

// Old code
ServerBrowser serverBrowser = new ServerBrowser();
serverBrowser.setModal(true);
serverBrowser.setVisible(true);

// New code
ServerBrowser serverBrowser = new ServerBrowser();
serverBrowser.setModal(false);
serverBrowser.setVisible(true);

With this change, the ServerBrowser will no longer block the UI thread. Next, we need to ensure that when the user selects a server and clicks the "Join" button, the SignOn dialog is instantiated directly from the ServerBrowser. This eliminates the need for global state or configuration to pass server information.

// In ServerBrowser.java

private void onJoinButtonClicked() {
    String serverAddress = getServerAddress();
    String serverPort = getServerPort();
    SignOnDialog signOnDialog = new SignOnDialog(serverAddress, serverPort);
    signOnDialog.setVisible(true);
}

Streamlining the SignOn Dialog

Next up is the SignOn dialog. Currently, it pulls user information like username and password from the configuration. We want to change this so that the SignOn dialog receives this information directly through its constructor. This makes the dialog more self-contained and easier to test.

// Old code
SignOnDialog signOnDialog = new SignOnDialog();
String username = Config.getUsername();
String password = Config.getPassword();
signOnDialog.setUsername(username);
signOnDialog.setPassword(password);

// New code
SignOnDialog signOnDialog = new SignOnDialog(serverAddress, serverPort, username, password);

By passing the username and password directly to the constructor, we eliminate the need for the SignOn dialog to access the configuration. This reduces coupling and makes the code more modular. Additionally, when the user clicks the "Connect" button, the SignOn dialog should directly instantiate the SplashWindow. This continues the chain of direct instantiation and data passing.

// In SignOnDialog.java

private void onConnectButtonClicked() {
    String username = getUsername();
    String password = getPassword();
    SplashWindow splashWindow = new SplashWindow(username, password, serverAddress, serverPort);
    splashWindow.setVisible(true);
    this.dispose(); // Close the SignOnDialog
}

Enhancing the SplashWindow

The SplashWindow is responsible for displaying the loading screen while the client connects to the server. Once the connection is established and the loading is complete, the SplashWindow should directly instantiate the MainMenu. This ensures a seamless transition from the loading screen to the main menu.

// In SplashWindow.java

public void onLoadingComplete() {
    MainMenu mainMenu = new MainMenu(userData);
    mainMenu.setVisible(true);
    this.dispose(); // Close the SplashWindow
}

By directly instantiating the MainMenu, the SplashWindow maintains the chain of direct instantiation and data passing. This makes the code more modular and easier to maintain.

Acceptance Criteria

To ensure that our refactoring is successful, we need to meet the following acceptance criteria:

  • Non-Blocking Dialogs: Both the ServerBrowser and SignOn dialogs should not have modal set to true and block when setVisible(true).
  • Direct Instantiation: The ServerBrowser should directly instantiate the SignOn dialog when join is selected.
  • Constructor Data Passing: The SignOn dialog constructor should be passed information like username and password instead of taking it from the config.
  • SplashWindow Instantiation: The SignOn dialog should directly instantiate the SplashWindow when connect is clicked.
  • MainMenu Instantiation: The SplashWindow should directly instantiate the MainMenu when it is completed.

Benefits of the Refactoring

By refactoring the client initialization flow, we achieve several benefits:

  • Improved Responsiveness: Non-blocking dialogs allow the user to interact with other parts of the application while the dialogs are open.
  • Reduced Coupling: Direct instantiation and data passing reduce the coupling between components, making the code more modular and easier to maintain.
  • Increased Testability: The code becomes easier to test because the components are more self-contained and don't rely on global state or configuration.
  • Enhanced Maintainability: Changes to one component are less likely to have unintended consequences on others due to the reduced coupling.

Step-by-Step Implementation Guide

To implement this refactoring, follow these steps:

  1. Modify the ServerBrowser:
    • Set the modal property to false.
    • Implement the onJoinButtonClicked method to instantiate the SignOn dialog directly.
  2. Update the SignOn Dialog:
    • Modify the constructor to accept username, password, server address, and server port as parameters.
    • Implement the onConnectButtonClicked method to instantiate the SplashWindow directly.
  3. Enhance the SplashWindow:
    • Implement the onLoadingComplete method to instantiate the MainMenu directly.
  4. Test the Changes:
    • Verify that the ServerBrowser and SignOn dialogs are non-blocking.
    • Ensure that the data is passed correctly between the components.
    • Confirm that the MainMenu is displayed after the SplashWindow completes.

Code Examples

Here are some code examples to illustrate the changes:

ServerBrowser.java

public class ServerBrowser extends JDialog {
    // ...

    private void onJoinButtonClicked() {
        String serverAddress = getServerAddress();
        String serverPort = getServerPort();
        SignOnDialog signOnDialog = new SignOnDialog(serverAddress, serverPort, "defaultUser", "password");
        signOnDialog.setVisible(true);
    }
}

SignOnDialog.java

public class SignOnDialog extends JDialog {
    private String serverAddress;
    private String serverPort;
    private String username;
    private String password;

    public SignOnDialog(String serverAddress, String serverPort, String username, String password) {
        this.serverAddress = serverAddress;
        this.serverPort = serverPort;
        this.username = username;
        this.password = password;
        // ...
    }

    private void onConnectButtonClicked() {
        SplashWindow splashWindow = new SplashWindow(username, password, serverAddress, serverPort);
        splashWindow.setVisible(true);
        this.dispose();
    }
}

SplashWindow.java

public class SplashWindow extends JDialog {
    // ...

    public void onLoadingComplete() {
        MainMenu mainMenu = new MainMenu(userData);
        mainMenu.setVisible(true);
        this.dispose();
    }
}

Conclusion

By refactoring the client initialization flow, we can create a more responsive, maintainable, and testable application. The key is to use direct instantiation and data passing to reduce coupling between components. This approach not only improves the user experience but also makes the codebase easier to work with in the long run. So, let's get to work and make these improvements!