Decoupling GUI and database components

Our current code lacks any kind of error handling: Exceptions will not be caught at all and invariably lead to program termination. This is of course inadequate with respect to professional software. In case of problems we have to:

  • Gracefully recover or shut down our application. We may for example show a pop up window Terminating due to an internal error.

  • Enable the customer to supply the development team with helpful information. The user may for example be asked to submit a log file in case of errors.

In addition the solution does contain an ugly mix of GUI components and database related code. We take a first step to decouple these two distinct concerns:

exercise No. 47

Database layer handling Create comment in forum

Q:

Implement a class PersistenceHandler to be used as a component of our next step GUI application prototype. This class should have the following methods:

public class PersistenceHandler {

   /**
    * New instances are in <strong>disconnected</strong> state. See {@link #isConnected()}
    * @param loginView Forward both informative and error messages plus status updates.
    */
   public PersistenceHandler(final PersistView loginView) { //TODO;}

/**
    * Inserting a (name, email) record into the database server. Errors are being logged and forwarded
    * to {@link LoginUI#guiError(String)}.
    *
    * <dl>
    *   <dt><b>Precondition:</b></dt>
    *   <dd>must be in <strong>connected</strong> state, see {@link #toggleConnectionState()}</dd>
    * </dl>
    *
    * @param name A person's name
    * @param email A person's email address
    *
    */
   public boolean insert(final String name, final String email) {// TODO ...
   }
/**
    * @return connected to database server?
    */
   public boolean isConnected() { // TODO
   }
   /**
    * Switch between connected and disconnected state.
    */
   public void toggleConnectionState() {// TODO
   }
}

Notice the two internal states disconnected and connected:

Figure 947. PersistenceHandler state transitions.
PersistenceHandler state transitions.

According to the above sketch a newly created PersistenceHandler instance should be in disconnected state. As being shown in the Java class description you may test your implementation without any GUI code.

The Interface PersistView allows to forward both messages and status updates from the database to the GUI layer. If e.g. a link layer exception occurs an informative error message may be forwarded by calling guiError(...):

/**
 * Communication between persistence and GUI layer
 *
 */
public interface PersistView {

      /**
       * Turn GUI into "connected" status
       */
      public abstract void setStatusConnected();

      /**
       * Turn GUI into "disconnected" status
       */
      public abstract void setStatusDisconnected();

      /**
       * Provide GUI status update.
       */
      public abstract void setDatasetInserted();

      /**
       * Provide GUI status update including informative message.
       * @param msg The informative message in question.
       */
      public abstract void guiInfo(String msg);

      /**
       * Provide GUI status update including error message.
       * @param msg The error message in question.
       */
      public abstract void guiError(String msg);
}

You may implement this interface by a mocking class for test purposes:

/**
 * Mimic a GUI frontend.
 *
 */
public class ViewMockup implements PersistView {

   ...

   @Override
   public void setStatusConnected() {
      ...
   }

   @Override
   public void setStatusDisconnected() {
      ...
   }

...

This allows for meaningful unit tests including end user messages:

public class DataInsertTest {

   final ViewMockup viewMockup = new ViewMockup();

   final PersistenceHandler ph = new PersistenceHandler(viewMockup);

   /**
    */
   @Test ()
   public void addDatasets() {
      try {
         initDatabase();
      } catch (SQLException e) {
         Assert.fail("Unable to initialize database:" + e.getLocalizedMessage());
         return;
      }
      ph.toggleConnectionState();
      Assert.assertTrue(ph.insert("Anton", "a@mail.com"));

      // Failure expected due to primary key constraint violation
      Assert.assertFalse(ph.insert("Anton", "a@mail.com"));
      Assert.assertNotNull(viewMockup.error);
      Assert.assertEquals(
            "E-mail already in database",
            viewMockup.error);
      ...

A:

We may now complete the next GUI database client enhancement step.