Database TDD Part 9: Single Responsibility Principle

by Jeff Langr

October 24, 2005

The User class is still doing too much by managing its own persistence. Following the SRP, I’ve redesigned the classes so that the User class only manages user information, and a new class, UserAccess, persists users. In the new design, the User class knows nothing about the persistence mechanism whatsoever. This does change the client interface, mind you.

UserTest

    package domain;
    
    import junit.framework.*;
    
    public class UserTest extends TestCase {
       static final String name = "a";
       static final String password = "b";
       private User user;
    
       protected void setUp() {
          user = new User(name, password);
       }
    
       public void testCreate() {
          assertEquals(name, user.getName());
          assertEquals(password, user.getPassword());
       }
    }

User

    package domain;
    
    public class User {
       private String name;
       private String password;
    
       public User(String name, String password) {
          this.name = name;
          this.password = password;
       }
    
       public String getName() {
          return name;
       }
    
       public String getPassword() {
          return password;
       }
    }

UserAccessTest

    package domain;
    
    import junit.framework.*;
    
    public class UserAccessTest extends TestCase {
       public void testPersist() {
          final String name = "a";
          final String password = "b";
    
          User user = new User(name, password);
          UserAccess access = new UserAccess();
          access.save(user);
          User retrievedUser = access.find(name);
          assertEquals(name, retrievedUser.getName());
          assertEquals(password, retrievedUser.getPassword());
       }
    }

UserAccess

    package domain;
    
    import java.util.*;
    
    import persistence.*;
    import util.*;
    
    public class UserAccess {
       private static final String TABLE_NAME = "userdata";
       private static String[] columns = { "name", "password" };
    
       public void save(User user) {
          new JdbcAccess().execute("insert into " + TABLE_NAME + " ("
                + createColumnList()  + ") values ("
                + createValuesList(user) + ")");
       }
    
       private String createValuesList(User user) {
          Transformer ticWrapper = new Transformer() {
             public String transform(String input) {
                return StringUtil.wrap(input, '\'');
             }
          };
          return StringUtil.commaDelimit(new String[] { user.getName(),
                user.getPassword() }, ticWrapper);
       }
    
       public User find(String nameKey) {
          JdbcAccess access = new JdbcAccess();
          List row = access.executeQuery(String.format(
                "select " + createColumnList() + " from " + TABLE_NAME + " where name = '%s'",
                nameKey));
          return new User(row.get(0), row.get(1));
       }
    
       private static String createColumnList() {
          return StringUtil.commaDelimit(columns);
       }
    }

A minor refactoring followed. I don’t recall how I got there, but my use of the String format method (introduced in J2SE 5.0) is less than ideal. The save and find methods can be cleaned up so that the SQL strings are much easier to read.

       public void save(User user) {
          String sql = String.format("insert into %s (%s) values (%s)", TABLE_NAME,
                createColumnList(), createValuesList(user));
          new JdbcAccess().execute(sql);
       }
    ...
       public User find(String nameKey) {
          String sql = String.format("select %s from %s where name = '%s'",
                createColumnList(), TABLE_NAME, nameKey);
          JdbcAccess access = new JdbcAccess();
          List<String> row = access.executeQuery(sql);
          return new User(row.get(0), row.get(1));
       }

So far, nothing earth shattering. The more interesting challenge is up next: we have to persist objects of a second type. The imminent redundancy should already be apparent. How will we keep UserAccess and whatever new access class from looking almost exactly alike?

Share your comment

Jeff Langr

About the Author

Jeff Langr has been building software for 40 years and writing about it heavily for 20. You can find out more about Jeff, learn from the many helpful articles and books he's written, or read one of his 1000+ combined blog (including Agile in a Flash) and public posts.