The Designer’s Block – Coooooode

What happened?  Did i fall off the end of the earth?  Dunno :-(.  Lets just continue as if we never stopped…..

So where were we.  Ah yes, we were going to take up a case study – My Stash and use it to try to understand the SOLID principles.  So lets dive right into it.

Lets start with 3 very simple things you would like My Stash to do.  Well, at the very least, it should allow me to:

  • enter an expense I have made
  • enter an income of some amount I received
  • and of course, track my overall balance

That’s simple and sweet.

Given that you are well versed with basic Java programming, let’s see what an implementation of the above would take in its raw-est of forms, assuming you are NOT aware of the SOLID principles and make very little use of object-oriented programming practices.

Ignoring the code that implements the User Interface, making use of basic Java, we would probably define a service called MyStashService, having one method corresponding to each requirement above as shown below:

public class MyStashService {

    // Record an income
    public void credit(double theAmount) { 
        // TBD 
    }

    // Record expense
    public void debit(double theAmount) { 
        // TBD 
    }

    // Track my balance
    public double getBalance() { 
        // TBD 
        return 0.0d;
    }
}

Assuming a database to store all information and using JDBC to query and update it, lets start the implementation for the afore mentioned class.  First the constructor, to setup initialize the database connectivity parameters:

public class MyStashService {

	private String dbConnectionUrl;

	private String dbUserName;

	private String dbPassword;

	public MyStashService(
            String theDBConnectionUrl, 
            String theDBUser,
	    String theDBPassword) 
            throws ClassNotFoundException {

		// DB Connection Information
		dbConnectionUrl = theDBConnectionUrl;
		dbUserName = theDBUser;
		dbPassword = theDBPassword;

		// Register Driver
		Class.forName("org.postgresql.Driver");
	}
}

Let’s start implementing each method.

NOTE: Being a developer myself,  I can understand the instinct that some of you may have to go through each line of code I am listing, sometimes in search of a bug or making faces at certain styles you see.  The point is to get a general feel of what it takes to put together a Java program that can achieve some meaningful functionality.

First the credit() method:

public void credit(double theAmount) throws SQLException {

    // Get Balance
    double balance;
    try (Connection conn = DriverManager.getConnection(
        dbConnectionUrl, dbUserName, dbPassword)) {

        conn.setAutoCommit(false);

        try (PreparedStatement stmt = conn.prepareStatement(
                "select balance from mystash");
             ResultSet rs = stmt.executeQuery();) {

            rs.next();
            balance = rs.getDouble("balance");
            conn.commit();

        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    // Increase the balance by the amount
    balance = balance + theAmount;

    // Update Balance
    try (Connection conn = DriverManager.getConnection(
        dbConnectionUrl, dbUserName, dbPassword)) {

        conn.setAutoCommit(false);

        try (PreparedStatement stmt = conn.prepareStatement(
            "update mystash set balance = ?")) {

            stmt.setDouble(1, balance);
            int rowsUpdated = stmt.executeUpdate();
            if (rowsUpdated != 1) {
                throw new SQLException("Unexpected error.  " + 
                    "One row should have been updated, but was " + 
                    rowsUpdated);
            }

            conn.commit();

        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }
}

Wow! That was a long method. Thanks to the Java 8 of try-with-resources the method is actually not as long as it would have been.

Now the debit() method:

public void debit(BigDecimal theAmount) throws SQLException {

    // Get Balance
    double balance;
    try (Connection conn = DriverManager.getConnection(
        dbConnectionUrl, dbUserName, dbPassword)) {

        conn.setAutoCommit(false);

        try (PreparedStatement stmt = conn.prepareStatement(
                 "select balance from mystash");
             ResultSet rs = stmt.executeQuery();) {

            rs.next();
            balance = rs.getDouble("balance");
            conn.commit();

        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }

    // Reduce the balance by the amount
    balance = balance - theAmount;

    // Update Balance
    try (Connection conn = DriverManager.getConnection(
        dbConnectionUrl, dbUserName, dbPassword)) {

        conn.setAutoCommit(false);

        try (PreparedStatement stmt = conn.prepareStatement(
            "update mystash set balance = ?")) {

            stmt.setDouble(1, balance);
            int rowsUpdated = stmt.executeUpdate();
            if (rowsUpdated != 1) {
                throw new SQLException("Unexpected error.  " + 
                    "One row should have been updated, but was " + 
                    rowsUpdated);
            }

            conn.commit();

        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }
}

That was another long method.

And finally, the getBalance() method…familiar code we have already written…but we are not going to apply the DRY best practice (please look it up) just yet:

public double getBalance() throws SQLException {
    
    // Get Balance
    try (Connection conn = DriverManager.getConnection(
        dbConnectionUrl, dbUserName, dbPassword)) {

        conn.setAutoCommit(false);

        try (PreparedStatement stmt = conn.prepareStatement(
                 "select balance from mystash");
             ResultSet rs = stmt.executeQuery();) {

            rs.next();
            double balance = rs.getDouble("balance");
            conn.commit();

            return balance;

        } catch (SQLException e) {
            conn.rollback();
            throw e;
        }
    }
}

That was the last one.

Check your IDE line count. Mine shows about 140+. The class however gets the job done and at least has some structure and comments. My earlier version – before Java 8 try-with-resources was released was much larger as I had if else statements around the Connection, ResultSet, etc, checking if they were null.

Imagine what this class is going to look like over time as more methods get added, multiple developers make modifications to it, they fix issues; add their own hacks and logic style; follow their own coding standards and conventions. You may have noticed that I am not even maintaining a transaction log, sending out notifications to the account holder,checking for balance before doing a debit, etc.  It’s going to soon become a maintenance nightmare. Forget about being able to Unit Test any of the code. They have a term for such kind of code. It’s called Spaghetti Code.  Its like a huge bowl of noodles!

Let’s look at the classes created so far. After all we have completed implementing the service layer containing the basic set of use-cases for a usable “My Stash” application. Just one class “MyStashService”.

I thought we were using Java, an OO programming language? Definitely something is amiss.

Time for “Operation Clean Up!”,  Time to implement something “SOLID” in Part 3.  I will let you mull over what we have done so far.

Leave a comment